RAPI: Implement an instance reboot
[ganeti-local] / lib / rapi / rlib2.py
index 956d2bf..1556cfb 100644 (file)
 
 """
 
-import re
-
 import ganeti.opcodes
-
-from ganeti import constants
+from ganeti import http
 from ganeti import luxi
+from ganeti import constants
+from ganeti.rapi import baserlib
 
-from ganeti.rapi import baserlib 
+from ganeti.rapi.rlib1 import I_FIELDS, N_FIELDS
 
 
 class R_2_jobs(baserlib.R_Generic):
@@ -44,7 +43,7 @@ class R_2_jobs(baserlib.R_Generic):
 
     Returns:
       A dictionary with jobs id and uri.
-    
+
     """
     fields = ["id"]
     # Convert the list of lists to the list of ids
@@ -61,21 +60,21 @@ class R_2_jobs_id(baserlib.R_Generic):
   def GET(self):
     """Returns a job status.
 
-    Returns: 
+    Returns:
       A dictionary with job parameters.
 
     The result includes:
       id - job ID as a number
       status - current job status as a string
-      ops - involved OpCodes as a list of dictionaries for each opcodes in 
+      ops - involved OpCodes as a list of dictionaries for each opcodes in
         the job
       opstatus - OpCodes status as a list
       opresult - OpCodes results as a list of lists
-    
+
     """
     fields = ["id", "ops", "status", "opstatus", "opresult"]
     job_id = self.items[0]
-    result = luxi.Client().QueryJobs([job_id,], fields)[0]
+    result = luxi.Client().QueryJobs([job_id, ], fields)[0]
     return baserlib.MapFields(fields, result)
 
 
@@ -84,34 +83,10 @@ class R_2_nodes(baserlib.R_Generic):
 
   """
   DOC_URI = "/2/nodes"
-  def _GetDetails(self, nodeslist):
-    """Returns detailed instance data for bulk output.
-
-    Args:
-      instance: A list of nodes names.
-
-    Returns:
-      A list of nodes properties
 
-    """
-    fields = ["name","dtotal", "dfree",
-              "mtotal", "mnode", "mfree",
-              "pinst_cnt", "sinst_cnt", "tags"]
-
-    op = ganeti.opcodes.OpQueryNodes(output_fields=fields,
-                                     names=nodeslist)
-    result = ganeti.cli.SubmitOpCode(op)
-
-    nodes_details = []
-    for node in result:
-      mapped = baserlib.MapFields(fields, node)
-      nodes_details.append(mapped)
-    return nodes_details
   def GET(self):
     """Returns a list of all nodes.
-    
+
     Returns:
       A dictionary with 'name' and 'uri' keys for each of them.
 
@@ -125,7 +100,7 @@ class R_2_nodes(baserlib.R_Generic):
           "uri": "\/instances\/node2.example.com"
         }]
 
-    If the optional 'bulk' argument is provided and set to 'true' 
+    If the optional 'bulk' argument is provided and set to 'true'
     value (i.e '?bulk=1'), the output contains detailed
     information about nodes as a list.
 
@@ -147,8 +122,192 @@ class R_2_nodes(baserlib.R_Generic):
     """
     op = ganeti.opcodes.OpQueryNodes(output_fields=["name"], names=[])
     nodeslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0)
-    
+
     if 'bulk' in self.queryargs:
-      return self._GetDetails(nodeslist)
+      op = ganeti.opcodes.OpQueryNodes(output_fields=N_FIELDS,
+                                       names=nodeslist)
+      result = ganeti.cli.SubmitOpCode(op)
+      return baserlib.MapBulkFields(result, N_FIELDS)
+
+    return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
+                                 uri_fields=("id", "uri"))
+
 
-    return baserlib.BuildUriList(nodeslist, "/nodes/%s", uri_fields=("id", "uri"))
+class R_2_instances(baserlib.R_Generic):
+  """/2/instances resource.
+
+  """
+  DOC_URI = "/2/instances"
+
+  def GET(self):
+    """Returns a list of all available instances.
+
+    Returns:
+       A dictionary with 'name' and 'uri' keys for each of them.
+
+    Example: [
+        {
+          "name": "web.example.com",
+          "uri": "\/instances\/web.example.com"
+        },
+        {
+          "name": "mail.example.com",
+          "uri": "\/instances\/mail.example.com"
+        }]
+
+    If the optional 'bulk' argument is provided and set to 'true'
+    value (i.e '?bulk=1'), the output contains detailed
+    information about instances as a list.
+
+    Example: [
+        {
+           "status": "running",
+           "bridge": "xen-br0",
+           "name": "web.example.com",
+           "tags": ["tag1", "tag2"],
+           "admin_ram": 512,
+           "sda_size": 20480,
+           "pnode": "node1.example.com",
+           "mac": "01:23:45:67:89:01",
+           "sdb_size": 4096,
+           "snodes": ["node2.example.com"],
+           "disk_template": "drbd",
+           "ip": null,
+           "admin_state": true,
+           "os": "debian-etch",
+           "vcpus": 2,
+           "oper_state": true
+        },
+        ...
+    ]
+
+    """
+    op = ganeti.opcodes.OpQueryInstances(output_fields=["name"], names=[])
+    instanceslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0)
+
+    if 'bulk' in self.queryargs:
+      op = ganeti.opcodes.OpQueryInstances(output_fields=I_FIELDS,
+                                           names=instanceslist)
+      result = ganeti.cli.SubmitOpCode(op)
+      return baserlib.MapBulkFields(result, I_FIELDS)
+
+
+    else:
+      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
+                                   uri_fields=("id", "uri"))
+
+  def PUT(self):
+    """Create an instance.
+
+    Returns:
+      A job id.
+
+    """
+    opts = self.req.request_post_data
+
+    # beparams
+    mem = opts.get('mem', None)
+    vcpus = opts.get('vcpus', None)
+    auto_balance = opts.get('auto_balance', None)
+
+    beparams = {}
+
+    for key, const in [(mem, constants.BE_MEMORY),
+                       (vcpus, constants.BE_VCPUS),
+                       (auto_balance, constants.BE_AUTO_BALANCE)]:
+      if key is not None:
+        beparams[const] = key
+
+    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=opts.get('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
+
+
+class R_2_instances_name_reboot(baserlib.R_Generic):
+  """/2/instances/[instance_name]/reboot resource.
+
+  Implements an instance reboot.
+
+  """
+
+  DOC_URI = "/2/instances/[instance_name]/reboot"
+
+  def GET(self):
+    """Reboot an instance.
+
+    """
+    instance_name = self.items[0]
+    reboot_type = self.queryargs.get('reboot_type',
+                                     constants.INSTANCE_REBOOT_HARD)
+    ignore_secondaries = self.queryargs.get('ignore_secondaries', False)
+    op = ganeti.opcodes.OpRebootInstance(
+        instance_name=instance_name,
+        reboot_type=reboot_type,
+        ignore_secondaries=ignore_secondaries)
+
+    job_id = ganeti.cli.SendJob([op])
+
+    return job_id
+
+
+class R_2_instances_name_tags(baserlib.R_Generic):
+  """/2/instances/[instance_name]/tags resource.
+
+  Manages per-instance tags.
+
+  """
+  DOC_URI = "/2/instances/[instance_name]/tags"
+
+  def GET(self):
+    """Returns a list of instance tags.
+
+    Example: ["tag1", "tag2", "tag3"]
+
+    """
+    return baserlib._Tags_GET(constants.TAG_INSTANCE, name=self.items[0])
+
+  def POST(self):
+    """Add a set of tags to the instance.
+
+    The reqest as a list of strings should be POST to this URI. And you'll have
+    back a job id.
+
+    """
+    return baserlib._Tags_POST(constants.TAG_INSTANCE,
+                               self.post_data, name=self.items[0])
+
+  def DELETE(self):
+    """Delete a tag.
+
+    In order to delete a set of tags from a instance, DELETE request should be
+    addressed to URI like: /2/instances/[instance_name]/tags?tag=[tag]&tag=[tag]
+
+    """
+    if 'tag' not in self.queryargs:
+      # no we not gonna delete all tags from an instance
+      raise http.HTTPNotImplemented()
+    return baserlib._Tags_DELETE(constants.TAG_INSTANCE,
+                                 self.queryargs['tag'],
+                                 name=self.items[0])