On Wed, May 12, 2010 at 7:08 PM, Michael Hanselmann <[email protected]> wrote: > Currently this only supports the new instance creation request > data format version 1, but support for the old version will be > easily implemented.
s/will/can/ unless you're going to do it. :) > > Most arguments are optional and documented in the RAPI documentation. > > Signed-off-by: Michael Hanselmann <[email protected]> > --- > lib/rapi/client.py | 55 +++++++++++++++++++++++++++++++--- > test/ganeti.rapi.client_unittest.py | 42 +++++++++++++++++++++++++- > 2 files changed, 90 insertions(+), 7 deletions(-) > > diff --git a/lib/rapi/client.py b/lib/rapi/client.py > index 97d89e0..dadb820 100644 > --- a/lib/rapi/client.py > +++ b/lib/rapi/client.py > @@ -56,6 +56,10 @@ NODE_ROLE_MASTER = "master" > NODE_ROLE_OFFLINE = "offline" > NODE_ROLE_REGULAR = "regular" > > +# Internal constants > +_REQ_DATA_VERSION_FIELD = "__version__" > +_INST_CREATE_REQV1 = "instance-create-reqv1" > + > > class Error(Exception): > """Base error class for this module. > @@ -555,23 +559,64 @@ class GanetiRapiClient(object): > ("/%s/instances/%s" % > (GANETI_RAPI_VERSION, instance)), None, None) > > - def CreateInstance(self, dry_run=False): > + def CreateInstance(self, mode, name, disk_template, disks, nics, > + **kwargs): > """Creates a new instance. > > + More details for parameters can be found in the RAPI documentation. > + > + �...@type mode: string > + �...@param mode: Instance creation mode > + �...@type name: string > + �...@param name: Hostname of the instance to create > + �...@type disk_template: string > + �...@param disk_template: Disk template for instance (e.g. plain, > diskless, > + file, or drbd) > + �...@type disks: list of dicts > + �...@param disks: List of disk definitions > + �...@type nics: list of dicts > + �...@param nics: List of NIC definitions > @type dry_run: bool > - �...@param dry_run: whether to perform a dry run > + �...@keyword dry_run: whether to perform a dry run > > @rtype: int > @return: job id > > """ > - # TODO: Pass arguments needed to actually create an instance. > query = [] > - if dry_run: > + > + if kwargs.get("dry_run"): > query.append(("dry-run", 1)) > > + if _INST_CREATE_REQV1 in self.GetFeatures(): > + # All required fields for request data version 1 > + body = { > + _REQ_DATA_VERSION_FIELD: 1, > + "mode": mode, > + "name": name, > + "disk_template": disk_template, > + "disks": disks, > + "nics": nics, > + } > + > + conflicts = set(kwargs.iterkeys()) & set(body.iterkeys()) > + if conflicts: > + raise GanetiApiError("Required fields can not be specified as" > + " keywords: %s" % ", ".join(conflicts)) > + > + body.update((key, value) for key, value in kwargs.iteritems() > + if key != "dry_run") > + else: > + # TODO: Implement instance creation request data version 0 > + # When implementing version 0, care should be taken to refuse unknown > + # parameters and invalid values. The interface of this function must > stay > + # exactly the same for version 0 and 1 (e.g. they aren't allowed to > + # require different data types). > + raise NotImplementedError("Support for instance creation request data" > + " version 0 is not yet implemented") > + > return self._SendRequest(HTTP_POST, "/%s/instances" % GANETI_RAPI_VERSION, > - query, None) > + query, body) > > def DeleteInstance(self, instance, dry_run=False): > """Deletes an instance. > diff --git a/test/ganeti.rapi.client_unittest.py > b/test/ganeti.rapi.client_unittest.py > index 8840a4b..f06a5bf 100755 > --- a/test/ganeti.rapi.client_unittest.py > +++ b/test/ganeti.rapi.client_unittest.py > @@ -239,12 +239,50 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): > self.assertHandler(rlib2.R_2_instances_name) > self.assertItems(["instance"]) > > + def testCreateInstanceOldVersion(self): > + self.rapi.AddResponse(serializer.DumpJson([])) > + self.assertRaises(NotImplementedError, self.client.CreateInstance, > + "create", "inst1.example.com", "plain", [], [], > + dry_run=True) > + > def testCreateInstance(self): > - self.rapi.AddResponse("1234") > - self.assertEqual(1234, self.client.CreateInstance(dry_run=True)) > + self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1])) > + self.rapi.AddResponse("23030") > + job_id = self.client.CreateInstance("create", "inst1.example.com", > + "plain", [], [], dry_run=True) > + self.assertEqual(job_id, 23030) > self.assertHandler(rlib2.R_2_instances) > self.assertDryRun() > > + data = serializer.LoadJson(self.http.last_request.data) > + > + for field in ["dry_run", "beparams", "hvparams", "start"]: > + self.assertFalse(field in data) > + > + self.assertEqual(data["name"], "inst1.example.com") > + self.assertEqual(data["disk_template"], "plain") > + > + def testCreateInstance2(self): > + self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1])) > + self.rapi.AddResponse("24740") > + job_id = self.client.CreateInstance("import", "inst2.example.com", > + "drbd8", [{"size": 100,}], > + [{}, {"bridge": "br1", }], > + dry_run=False, start=True, > + pnode="node1", snode="node9", > + ip_check=False) > + self.assertEqual(job_id, 24740) > + self.assertHandler(rlib2.R_2_instances) > + > + data = serializer.LoadJson(self.http.last_request.data) > + self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1) > + self.assertEqual(data["name"], "inst2.example.com") > + self.assertEqual(data["disk_template"], "drbd8") > + self.assertEqual(data["start"], True) > + self.assertEqual(data["ip_check"], False) > + self.assertEqualValues(data["disks"], [{"size": 100,}]) > + self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }]) > + > def testDeleteInstance(self): > self.rapi.AddResponse("1234") > self.assertEqual(1234, self.client.DeleteInstance("instance", > dry_run=True)) > -- > 1.7.0.4 > LGTM for the rest Thanks, Guido
