"""
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# C0103: Invalid name, since the R_* names are not conforming
"name",
"node_cnt",
"node_list",
+ "ipolicy",
] + _COMMON_FIELDS
J_FIELDS_BULK = [
return None
+class R_2(R_root):
+ """/2 resource.
+
+ """
+
+
class R_version(baserlib.ResourceBase):
"""/version resource.
return constants.RAPI_VERSION
-class R_2_info(baserlib.ResourceBase):
+class R_2_info(baserlib.OpcodeResource):
"""/2/info resource.
"""
+ GET_OPCODE = opcodes.OpClusterQuery
+
def GET(self):
"""Returns cluster information.
return list(ALL_FEATURES)
-class R_2_os(baserlib.ResourceBase):
+class R_2_os(baserlib.OpcodeResource):
"""/2/os resource.
"""
+ GET_OPCODE = opcodes.OpOsDiagnose
+
def GET(self):
"""Return a list of all OSes.
}
-class R_2_nodes(baserlib.ResourceBase):
+class R_2_nodes(baserlib.OpcodeResource):
"""/2/nodes resource.
"""
+ GET_OPCODE = opcodes.OpNodeQuery
+
def GET(self):
"""Returns a list of all nodes.
uri_fields=("id", "uri"))
-class R_2_nodes_name(baserlib.ResourceBase):
+class R_2_nodes_name(baserlib.OpcodeResource):
"""/2/nodes/[node_name] resource.
"""
+ GET_OPCODE = opcodes.OpNodeQuery
+
def GET(self):
"""Send information about a node.
return baserlib.MapFields(N_FIELDS, result[0])
+class R_2_nodes_name_powercycle(baserlib.OpcodeResource):
+ """/2/nodes/[node_name]/powercycle resource.
+
+ """
+ POST_OPCODE = opcodes.OpNodePowercycle
+
+ def GetPostOpInput(self):
+ """Tries to powercycle a node.
+
+ """
+ return (self.request_body, {
+ "node_name": self.items[0],
+ "force": self.useForce(),
+ })
+
+
class R_2_nodes_name_role(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/role resource.
"offline": offline,
"drained": drained,
"force": self.useForce(),
+ "auto_promote": bool(self._checkIntVariable("auto-promote", default=0)),
})
})
+class R_2_nodes_name_modify(baserlib.OpcodeResource):
+ """/2/nodes/[node_name]/modify resource.
+
+ """
+ POST_OPCODE = opcodes.OpNodeSetParams
+
+ def GetPostOpInput(self):
+ """Changes parameters of a node.
+
+ """
+ assert len(self.items) == 1
+
+ return (self.request_body, {
+ "node_name": self.items[0],
+ })
+
+
class R_2_nodes_name_storage(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/storage resource.
})
-class R_2_nodes_name_storage_modify(baserlib.ResourceBase):
+class R_2_nodes_name_storage_modify(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/storage/modify resource.
"""
- def PUT(self):
- node_name = self.items[0]
+ PUT_OPCODE = opcodes.OpNodeModifyStorage
- storage_type = self._checkStringVariable("storage_type", None)
- if not storage_type:
- raise http.HttpBadRequest("Missing the required 'storage_type'"
- " parameter")
+ def GetPutOpInput(self):
+ """Modifies a storage volume on a node.
+ """
+ storage_type = self._checkStringVariable("storage_type", None)
name = self._checkStringVariable("name", None)
+
if not name:
raise http.HttpBadRequest("Missing the required 'name'"
" parameter")
changes[constants.SF_ALLOCATABLE] = \
bool(self._checkIntVariable("allocatable", default=1))
- op = opcodes.OpNodeModifyStorage(node_name=node_name,
- storage_type=storage_type,
- name=name,
- changes=changes)
- return self.SubmitJob([op])
+ return ({}, {
+ "node_name": self.items[0],
+ "storage_type": storage_type,
+ "name": name,
+ "changes": changes,
+ })
-class R_2_nodes_name_storage_repair(baserlib.ResourceBase):
+class R_2_nodes_name_storage_repair(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/storage/repair resource.
"""
- def PUT(self):
- node_name = self.items[0]
+ PUT_OPCODE = opcodes.OpRepairNodeStorage
- storage_type = self._checkStringVariable("storage_type", None)
- if not storage_type:
- raise http.HttpBadRequest("Missing the required 'storage_type'"
- " parameter")
+ def GetPutOpInput(self):
+ """Repairs a storage volume on a node.
+ """
+ storage_type = self._checkStringVariable("storage_type", None)
name = self._checkStringVariable("name", None)
if not name:
raise http.HttpBadRequest("Missing the required 'name'"
" parameter")
- op = opcodes.OpRepairNodeStorage(node_name=node_name,
- storage_type=storage_type,
- name=name)
- return self.SubmitJob([op])
+ return ({}, {
+ "node_name": self.items[0],
+ "storage_type": storage_type,
+ "name": name,
+ })
class R_2_groups(baserlib.OpcodeResource):
"""/2/groups resource.
"""
+ GET_OPCODE = opcodes.OpGroupQuery
POST_OPCODE = opcodes.OpGroupAdd
POST_RENAME = {
"name": "group_name",
})
-def _ParseInstanceCreateRequestVersion1(data, dry_run):
- """Parses an instance creation request version 1.
-
- @rtype: L{opcodes.OpInstanceCreate}
- @return: Instance creation opcode
+class R_2_instances(baserlib.OpcodeResource):
+ """/2/instances resource.
"""
- override = {
- "dry_run": dry_run,
- }
-
- rename = {
+ GET_OPCODE = opcodes.OpInstanceQuery
+ POST_OPCODE = opcodes.OpInstanceCreate
+ POST_RENAME = {
"os": "os_type",
"name": "instance_name",
}
- return baserlib.FillOpcode(opcodes.OpInstanceCreate, data, override,
- rename=rename)
-
-
-class R_2_instances(baserlib.ResourceBase):
- """/2/instances resource.
-
- """
def GET(self):
"""Returns a list of all available instances.
return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
uri_fields=("id", "uri"))
- def POST(self):
+ def GetPostOpInput(self):
"""Create an instance.
@return: a job id
"""
- if not isinstance(self.request_body, dict):
- raise http.HttpBadRequest("Invalid body contents, not a dictionary")
+ baserlib.CheckType(self.request_body, dict, "Body contents")
# Default to request data version 0
data_version = self.getBodyParameter(_REQ_DATA_VERSION, 0)
if data_version == 0:
raise http.HttpBadRequest("Instance creation request version 0 is no"
" longer supported")
- elif data_version == 1:
- data = self.request_body.copy()
- # Remove "__version__"
- data.pop(_REQ_DATA_VERSION, None)
- op = _ParseInstanceCreateRequestVersion1(data, self.dryRun())
- else:
+ elif data_version != 1:
raise http.HttpBadRequest("Unsupported request data version %s" %
data_version)
- return self.SubmitJob([op])
+ data = self.request_body.copy()
+ # Remove "__version__"
+ data.pop(_REQ_DATA_VERSION, None)
+
+ return (data, {
+ "dry_run": self.dryRun(),
+ })
class R_2_instances_name(baserlib.OpcodeResource):
"""/2/instances/[instance_name] resource.
"""
+ GET_OPCODE = opcodes.OpInstanceQuery
DELETE_OPCODE = opcodes.OpInstanceRemove
def GET(self):
return ops
-class R_2_instances_name_reinstall(baserlib.ResourceBase):
+class R_2_instances_name_reinstall(baserlib.OpcodeResource):
"""/2/instances/[instance_name]/reinstall resource.
Implements an instance reinstall.
"""
+ POST_OPCODE = opcodes.OpInstanceReinstall
+
def POST(self):
"""Reinstall an instance.
"""Replaces disks on an instance.
"""
- data = self.request_body.copy()
static = {
"instance_name": self.items[0],
}
+ if self.request_body:
+ data = self.request_body
+ elif self.queryargs:
+ # Legacy interface, do not modify/extend
+ data = {
+ "remote_node": self._checkStringVariable("remote_node", default=None),
+ "mode": self._checkStringVariable("mode", default=None),
+ "disks": self._checkStringVariable("disks", default=None),
+ "iallocator": self._checkStringVariable("iallocator", default=None),
+ }
+ else:
+ data = {}
+
# Parse disks
try:
- raw_disks = data["disks"]
+ raw_disks = data.pop("disks")
except KeyError:
pass
else:
- if not ht.TListOf(ht.TInt)(raw_disks): # pylint: disable-msg=E1102
- # Backwards compatibility for strings of the format "1, 2, 3"
- try:
- data["disks"] = [int(part) for part in raw_disks.split(",")]
- except (TypeError, ValueError), err:
- raise http.HttpBadRequest("Invalid disk index passed: %s" % err)
+ if raw_disks:
+ if ht.TListOf(ht.TInt)(raw_disks): # pylint: disable=E1102
+ data["disks"] = raw_disks
+ else:
+ # Backwards compatibility for strings of the format "1, 2, 3"
+ try:
+ data["disks"] = [int(part) for part in raw_disks.split(",")]
+ except (TypeError, ValueError), err:
+ raise http.HttpBadRequest("Invalid disk index passed: %s" % err)
return (data, static)
})
+class R_2_instances_name_recreate_disks(baserlib.OpcodeResource):
+ """/2/instances/[instance_name]/recreate-disks resource.
+
+ """
+ POST_OPCODE = opcodes.OpInstanceRecreateDisks
+
+ def GetPostOpInput(self):
+ """Recreate disks for an instance.
+
+ """
+ return ({}, {
+ "instance_name": self.items[0],
+ })
+
+
class R_2_instances_name_prepare_export(baserlib.OpcodeResource):
"""/2/instances/[instance_name]/prepare-export resource.
"""
GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+ GET_OPCODE = opcodes.OpInstanceConsole
def GET(self):
"""Request information for connecting to instance's console.
"""
# Results might contain sensitive information
GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
+ GET_OPCODE = opcodes.OpQuery
+ PUT_OPCODE = opcodes.OpQuery
- def _Query(self, fields, filter_):
- return self.GetClient().Query(self.items[0], fields, filter_).ToDict()
+ def _Query(self, fields, qfilter):
+ return self.GetClient().Query(self.items[0], fields, qfilter).ToDict()
def GET(self):
"""Returns resource information.
except KeyError:
fields = _GetQueryFields(self.queryargs)
- return self._Query(fields, self.request_body.get("filter", None))
+ qfilter = body.get("qfilter", None)
+ # TODO: remove this after 2.7
+ if qfilter is None:
+ qfilter = body.get("filter", None)
+
+ return self._Query(fields, qfilter)
class R_2_query_fields(baserlib.ResourceBase):
"""/2/query/[resource]/fields resource.
"""
+ GET_OPCODE = opcodes.OpQueryFields
+
def GET(self):
"""Retrieves list of available fields for a resource.
return self.GetClient().QueryFields(self.items[0], fields).ToDict()
-class _R_Tags(baserlib.ResourceBase):
+class _R_Tags(baserlib.OpcodeResource):
""" Quasiclass for tagging resources
Manages tags. When inheriting this class you must define the
"""
TAG_LEVEL = None
+ GET_OPCODE = opcodes.OpTagsGet
+ PUT_OPCODE = opcodes.OpTagsSet
+ DELETE_OPCODE = opcodes.OpTagsDel
- def __init__(self, items, queryargs, req):
+ def __init__(self, items, queryargs, req, **kwargs):
"""A tag resource constructor.
We have to override the default to sort out cluster naming case.
"""
- baserlib.ResourceBase.__init__(self, items, queryargs, req)
+ baserlib.OpcodeResource.__init__(self, items, queryargs, req, **kwargs)
if self.TAG_LEVEL == constants.TAG_CLUSTER:
self.name = None
return list(tags)
- def PUT(self):
+ def GetPutOpInput(self):
"""Add a set of tags.
The request as a list of strings should be PUT to this URI. And
you'll have back a job id.
"""
- # pylint: disable-msg=W0212
- if "tag" not in self.queryargs:
- raise http.HttpBadRequest("Please specify tag(s) to add using the"
- " the 'tag' parameter")
- op = opcodes.OpTagsSet(kind=self.TAG_LEVEL, name=self.name,
- tags=self.queryargs["tag"], dry_run=self.dryRun())
- return self.SubmitJob([op])
+ return ({}, {
+ "kind": self.TAG_LEVEL,
+ "name": self.name,
+ "tags": self.queryargs.get("tag", []),
+ "dry_run": self.dryRun(),
+ })
- def DELETE(self):
+ def GetDeleteOpInput(self):
"""Delete a tag.
In order to delete a set of tags, the DELETE
/tags?tag=[tag]&tag=[tag]
"""
- # pylint: disable-msg=W0212
- if "tag" not in self.queryargs:
- # no we not gonna delete all tags
- raise http.HttpBadRequest("Cannot delete all tags - please specify"
- " tag(s) using the 'tag' parameter")
- op = opcodes.OpTagsDel(kind=self.TAG_LEVEL, name=self.name,
- tags=self.queryargs["tag"], dry_run=self.dryRun())
- return self.SubmitJob([op])
+ # Re-use code
+ return self.GetPutOpInput()
class R_2_instances_name_tags(_R_Tags):