Simplify handling of boolean args in rapi
[ganeti-local] / lib / rapi / rlib2.py
index d2e4051..8be65b6 100644 (file)
 
 """
 
-import ganeti.opcodes
+from ganeti import opcodes
 from ganeti import http
-from ganeti import luxi
 from ganeti import constants
+from ganeti import cli
 from ganeti.rapi import baserlib
 
 
+
 I_FIELDS = ["name", "admin_state", "os",
             "pnode", "snodes",
             "disk_template",
             "nic.ips", "nic.macs", "nic.bridges",
+            "network_port",
             "disk.sizes", "disk_usage",
             "beparams", "hvparams",
             "oper_state", "oper_ram", "status",
             "tags"]
 
-N_FIELDS = ["name", "offline", "master_candidate",
+N_FIELDS = ["name", "offline", "master_candidate", "drained",
             "dtotal", "dfree",
             "mtotal", "mnode", "mfree",
             "pinst_cnt", "sinst_cnt", "tags",
@@ -74,23 +76,37 @@ class R_2_info(baserlib.R_Generic):
 
     Example::
 
-      {
-        "config_version": 3,
-        "name": "cluster1.example.com",
-        "software_version": "1.2.4",
-        "os_api_version": 5,
-        "export_version": 0,
-        "master": "node1.example.com",
-        "architecture": [
-          "64bit",
-          "x86_64"
-        ],
-        "hypervisor_type": "xen-pvm",
-        "protocol_version": 12
+    {
+      "config_version": 2000000,
+      "name": "cluster",
+      "software_version": "2.0.0~beta2",
+      "os_api_version": 10,
+      "export_version": 0,
+      "candidate_pool_size": 10,
+      "enabled_hypervisors": [
+        "fake"
+      ],
+      "hvparams": {
+        "fake": {}
+       },
+      "default_hypervisor": "fake",
+      "master": "node1.example.com",
+      "architecture": [
+        "64bit",
+        "x86_64"
+      ],
+      "protocol_version": 20,
+      "beparams": {
+        "default": {
+          "auto_balance": true,
+          "vcpus": 1,
+          "memory": 128
+         }
+        }
       }
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
     return client.QueryClusterInfo()
 
 
@@ -108,12 +124,15 @@ class R_2_os(baserlib.R_Generic):
     Example: ["debian-etch"]
 
     """
-    op = ganeti.opcodes.OpDiagnoseOS(output_fields=["name", "valid"],
-                                     names=[])
-    diagnose_data = ganeti.cli.SubmitOpCode(op)
+    cl = baserlib.GetClient()
+    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
+    job_id = baserlib.SubmitJob([op], cl)
+    # we use custom feedback function, instead of print we log the status
+    result = cli.PollJob(job_id, cl, feedback_fn=baserlib.FeedbackFn)
+    diagnose_data = result[0]
 
     if not isinstance(diagnose_data, list):
-      raise http.HttpInternalServerError(message="Can't get OS list")
+      raise http.HttpBadGateway(message="Can't get OS list")
 
     return [row[0] for row in diagnose_data if row[1]]
 
@@ -131,8 +150,9 @@ class R_2_jobs(baserlib.R_Generic):
 
     """
     fields = ["id"]
+    cl = baserlib.GetClient()
     # Convert the list of lists to the list of ids
-    result = [job_id for [job_id] in luxi.Client().QueryJobs(None, fields)]
+    result = [job_id for [job_id] in cl.QueryJobs(None, fields)]
     return baserlib.BuildUriList(result, "/2/jobs/%s",
                                  uri_fields=("id", "uri"))
 
@@ -161,7 +181,7 @@ class R_2_jobs_id(baserlib.R_Generic):
               "received_ts", "start_ts", "end_ts",
               ]
     job_id = self.items[0]
-    result = luxi.Client().QueryJobs([job_id, ], fields)[0]
+    result = baserlib.GetClient().QueryJobs([job_id, ], fields)[0]
     if result is None:
       raise http.HttpNotFound()
     return baserlib.MapFields(fields, result)
@@ -171,7 +191,7 @@ class R_2_jobs_id(baserlib.R_Generic):
 
     """
     job_id = self.items[0]
-    result = luxi.Client().CancelJob(job_id)
+    result = baserlib.GetClient().CancelJob(job_id)
     return result
 
 
@@ -222,7 +242,7 @@ class R_2_nodes(baserlib.R_Generic):
     @return: a dictionary with 'name' and 'uri' keys for each of them
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
 
     if self.useBulk():
       bulkdata = client.QueryNodes([], N_FIELDS, False)
@@ -245,7 +265,7 @@ class R_2_nodes_name(baserlib.R_Generic):
 
     """
     node_name = self.items[0]
-    client = luxi.Client()
+    client = baserlib.GetClient()
     result = client.QueryNodes(names=[node_name], fields=N_FIELDS,
                                use_locking=self.useLocking())
 
@@ -284,29 +304,34 @@ class R_2_instances(baserlib.R_Generic):
       [
         {
            "status": "running",
-           "bridge": "xen-br0",
+           "disk_usage": 20480,
+           "nic.bridges": [
+             "xen-br0"
+            ],
            "name": "web.example.com",
            "tags": ["tag1", "tag2"],
-           "admin_ram": 512,
-           "sda_size": 20480,
+           "beparams": {
+             "vcpus": 2,
+             "memory": 512
+           },
+           "disk.sizes": [
+               20480
+           ],
            "pnode": "node1.example.com",
-           "mac": "01:23:45:67:89:01",
-           "sdb_size": 4096,
+           "nic.macs": ["01:23:45:67:89:01"],
            "snodes": ["node2.example.com"],
            "disk_template": "drbd",
-           "ip": null,
            "admin_state": true,
            "os": "debian-etch",
-           "vcpus": 2,
            "oper_state": true
         },
         ...
       ]
 
-    @returns: a dictionary with 'name' and 'uri' keys for each of them.
+    @return: a dictionary with 'name' and 'uri' keys for each of them.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
 
     use_locking = self.useLocking()
     if self.useBulk():
@@ -321,39 +346,54 @@ class R_2_instances(baserlib.R_Generic):
   def POST(self):
     """Create an instance.
 
-    @returns: a job id
+    @return: a job id
 
     """
-    opts = self.req.request_post_data
-
-    beparams = baserlib.MakeParamsDict(opts, constants.BES_PARAMETERS)
-    hvparams = baserlib.MakeParamsDict(opts, constants.HVS_PARAMETERS)
-
-    op = ganeti.opcodes.OpCreateInstance(
-        instance_name=opts.get('name'),
-        disk_size=opts.get('size', 20 * 1024),
-        swap_size=opts.get('swap', 4 * 1024),
-        disk_template=opts.get('disk_template', None),
-        mode=constants.INSTANCE_CREATE,
-        os_type=opts.get('os'),
-        pnode=opts.get('pnode'),
-        snode=opts.get('snode'),
-        ip=opts.get('ip', 'none'),
-        bridge=opts.get('bridge', None),
-        start=opts.get('start', True),
-        ip_check=opts.get('ip_check', True),
-        wait_for_sync=opts.get('wait_for_sync', True),
-        mac=opts.get('mac', 'auto'),
-        hypervisor=opts.get('hypervisor', None),
-        hvparams=hvparams,
-        beparams=beparams,
-        iallocator=opts.get('iallocator', None),
-        file_storage_dir=opts.get('file_storage_dir', None),
-        file_driver=opts.get('file_driver', 'loop'),
-        )
-
-    job_id = ganeti.cli.SendJob([op])
-    return job_id
+    if not isinstance(self.req.request_body, dict):
+      raise http.HttpBadRequest("Invalid body contents, not a dictionary")
+
+    beparams = baserlib.MakeParamsDict(self.req.request_body,
+                                       constants.BES_PARAMETERS)
+    hvparams = baserlib.MakeParamsDict(self.req.request_body,
+                                       constants.HVS_PARAMETERS)
+    fn = self.getBodyParameter
+
+    # disk processing
+    disk_data = fn('disks')
+    if not isinstance(disk_data, list):
+      raise http.HttpBadRequest("The 'disks' parameter should be a list")
+    disks = []
+    for idx, d in enumerate(disk_data):
+      if not isinstance(d, int):
+        raise http.HttpBadRequest("Disk %d specification wrong: should"
+                                  " be an integer")
+      disks.append({"size": d})
+    # nic processing (one nic only)
+    nics = [{"mac": fn("mac", constants.VALUE_AUTO),
+             "ip": fn("ip", None),
+             "bridge": fn("bridge", None)}]
+
+    op = opcodes.OpCreateInstance(
+      mode=constants.INSTANCE_CREATE,
+      instance_name=fn('name'),
+      disks=disks,
+      disk_template=fn('disk_template'),
+      os_type=fn('os'),
+      pnode=fn('pnode', None),
+      snode=fn('snode', None),
+      iallocator=fn('iallocator', None),
+      nics=nics,
+      start=fn('start', True),
+      ip_check=fn('ip_check', True),
+      wait_for_sync=True,
+      hypervisor=fn('hypervisor', None),
+      hvparams=hvparams,
+      beparams=beparams,
+      file_storage_dir=fn('file_storage_dir', None),
+      file_driver=fn('file_driver', 'loop'),
+      )
+
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name(baserlib.R_Generic):
@@ -366,13 +406,21 @@ class R_2_instances_name(baserlib.R_Generic):
     """Send information about an instance.
 
     """
-    client = luxi.Client()
+    client = baserlib.GetClient()
     instance_name = self.items[0]
     result = client.QueryInstances(names=[instance_name], fields=I_FIELDS,
                                    use_locking=self.useLocking())
 
     return baserlib.MapFields(I_FIELDS, result[0])
 
+  def DELETE(self):
+    """Delete an instance.
+
+    """
+    op = opcodes.OpRemoveInstance(instance_name=self.items[0],
+                                  ignore_failures=False)
+    return baserlib.SubmitJob([op])
+
 
 class R_2_instances_name_reboot(baserlib.R_Generic):
   """/2/instances/[instance_name]/reboot resource.
@@ -393,16 +441,12 @@ class R_2_instances_name_reboot(baserlib.R_Generic):
     instance_name = self.items[0]
     reboot_type = self.queryargs.get('type',
                                      [constants.INSTANCE_REBOOT_HARD])[0]
-    ignore_secondaries = bool(self.queryargs.get('ignore_secondaries',
-                                                 [False])[0])
-    op = ganeti.opcodes.OpRebootInstance(
-        instance_name=instance_name,
-        reboot_type=reboot_type,
-        ignore_secondaries=ignore_secondaries)
+    ignore_secondaries = bool(self._checkIntVariable('ignore_secondaries'))
+    op = opcodes.OpRebootInstance(instance_name=instance_name,
+                                  reboot_type=reboot_type,
+                                  ignore_secondaries=ignore_secondaries)
 
-    job_id = ganeti.cli.SendJob([op])
-
-    return job_id
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name_startup(baserlib.R_Generic):
@@ -422,13 +466,11 @@ class R_2_instances_name_startup(baserlib.R_Generic):
 
     """
     instance_name = self.items[0]
-    force_startup = bool(self.queryargs.get('force', [False])[0])
-    op = ganeti.opcodes.OpStartupInstance(instance_name=instance_name,
-                                          force=force_startup)
-
-    job_id = ganeti.cli.SendJob([op])
+    force_startup = bool(self._checkIntVariable('force'))
+    op = opcodes.OpStartupInstance(instance_name=instance_name,
+                                   force=force_startup)
 
-    return job_id
+    return baserlib.SubmitJob([op])
 
 
 class R_2_instances_name_shutdown(baserlib.R_Generic):
@@ -445,11 +487,39 @@ class R_2_instances_name_shutdown(baserlib.R_Generic):
 
     """
     instance_name = self.items[0]
-    op = ganeti.opcodes.OpShutdownInstance(instance_name=instance_name)
+    op = opcodes.OpShutdownInstance(instance_name=instance_name)
+
+    return baserlib.SubmitJob([op])
 
-    job_id = ganeti.cli.SendJob([op])
 
-    return job_id
+class R_2_instances_name_reinstall(baserlib.R_Generic):
+  """/2/instances/[instance_name]/reinstall resource.
+
+  Implements an instance reinstall.
+
+  """
+
+  DOC_URI = "/2/instances/[instance_name]/reinstall"
+
+  def POST(self):
+    """Reinstall an instance.
+
+    The URI takes os=name and nostartup=[0|1] optional
+    parameters. By default, the instance will be started
+    automatically.
+
+    """
+    instance_name = self.items[0]
+    ostype = self._checkStringVariable('os')
+    nostartup = self._checkIntVariable('nostartup')
+    ops = [
+      opcodes.OpShutdownInstance(instance_name=instance_name),
+      opcodes.OpReinstallInstance(instance_name=instance_name, os_type=ostype),
+      ]
+    if not nostartup:
+      ops.append(opcodes.OpStartupInstance(instance_name=instance_name,
+                                           force=False))
+    return baserlib.SubmitJob(ops)
 
 
 class _R_Tags(baserlib.R_Generic):
@@ -489,8 +559,11 @@ class _R_Tags(baserlib.R_Generic):
     you'll have back a job id.
 
     """
+    if 'tag' not in self.queryargs:
+      raise http.HttpBadRequest("Please specify tag(s) to add using the"
+                                " the 'tag' parameter")
     return baserlib._Tags_PUT(self.TAG_LEVEL,
-                              self.req.request_post_data, name=self.name)
+                              self.queryargs['tag'], name=self.name)
 
   def DELETE(self):
     """Delete a tag.
@@ -502,7 +575,8 @@ class _R_Tags(baserlib.R_Generic):
     """
     if 'tag' not in self.queryargs:
       # no we not gonna delete all tags
-      raise http.HttpNotImplemented()
+      raise http.HttpBadRequest("Cannot delete all tags - please specify"
+                                " tag(s) using the 'tag' parameter")
     return baserlib._Tags_DELETE(self.TAG_LEVEL,
                                  self.queryargs['tag'],
                                  name=self.name)