diff --git a/doc/rapi.rst b/doc/rapi.rst index 0e749122dbda550a82ee87859a42983f6654e2d0..a913b3215871724a6293f77f9fd836ce1e94ac87 100644 --- a/doc/rapi.rst +++ b/doc/rapi.rst @@ -464,6 +464,40 @@ Example:: ... ] +``/2/nodes/[node_name]/role`` ++++++++++++++++++++++++++++++ + +Manages node role. + +It supports the following commands: ``GET``, ``PUT``. + +The role is always one of the following: + + - drained + - master + - master-candidate + - offline + - regular + +``GET`` +~~~~~~~ + +Returns the current node role. + +Example:: + + "master-candidate" + +``PUT`` +~~~~~~~ + +Change the node role. + +The request is a string which should be PUT to this URI. The result will be a +job id. + +It supports the ``force`` argument. + ``/2/nodes/[node_name]/tags`` +++++++++++++++++++++++++++++ diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py index 43c15c0c74ebcae786466402b51cf4258083aae1..9c99dcfa6c9ccae0deabd0adbf531e6128f3f5b5 100644 --- a/lib/rapi/connector.py +++ b/lib/rapi/connector.py @@ -154,6 +154,7 @@ CONNECTOR.update({ "/2/nodes": rlib2.R_2_nodes, re.compile(r'^/2/nodes/([\w\._-]+)$'): rlib2.R_2_nodes_name, re.compile(r'^/2/nodes/([\w\._-]+)/tags$'): rlib2.R_2_nodes_name_tags, + re.compile(r'^/2/nodes/([\w\._-]+)/role$'): rlib2.R_2_nodes_name_role, "/2/instances": rlib2.R_2_instances, re.compile(r'^/2/instances/([\w\._-]+)$'): rlib2.R_2_instances_name, re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags, diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py index d79a0fc08888949e981b4de437d6c20340dd3e81..550692acd51e1be983fe7dd705e127e7de701011 100644 --- a/lib/rapi/rlib2.py +++ b/lib/rapi/rlib2.py @@ -30,7 +30,6 @@ from ganeti import cli from ganeti.rapi import baserlib - I_FIELDS = ["name", "admin_state", "os", "pnode", "snodes", "disk_template", @@ -48,6 +47,20 @@ N_FIELDS = ["name", "offline", "master_candidate", "drained", "ctotal", "cnodes", "csockets", ] +_NR_DRAINED = "drained" +_NR_MASTER_CANDIATE = "master-candidate" +_NR_MASTER = "master" +_NR_OFFLINE = "offline" +_NR_REGULAR = "regular" + +_NR_MAP = { + "M": _NR_MASTER, + "C": _NR_MASTER_CANDIATE, + "D": _NR_DRAINED, + "O": _NR_OFFLINE, + "R": _NR_REGULAR, + } + class R_version(baserlib.R_Generic): """/version resource. @@ -190,6 +203,64 @@ class R_2_nodes_name(baserlib.R_Generic): return baserlib.MapFields(N_FIELDS, result[0]) +class R_2_nodes_name_role(baserlib.R_Generic): + """ /2/nodes/[node_name]/role resource. + + """ + def GET(self): + """Returns the current node role. + + @return: Node role + + """ + node_name = self.items[0] + client = baserlib.GetClient() + result = client.QueryNodes(names=[node_name], fields=["role"], + use_locking=self.useLocking()) + + return _NR_MAP[result[0][0]] + + def PUT(self): + """Sets the node role. + + @return: a job id + + """ + if not isinstance(self.req.request_body, basestring): + raise http.HttpBadRequest("Invalid body contents, not a string") + + node_name = self.items[0] + role = self.req.request_body + + if role == _NR_REGULAR: + candidate = False + offline = False + drained = False + + elif role == _NR_MASTER_CANDIATE: + candidate = True + offline = drained = None + + elif role == _NR_DRAINED: + drained = True + candidate = offline = None + + elif role == _NR_OFFLINE: + offline = True + candidate = drained = None + + else: + raise http.HttpBadRequest("Can't set '%s' role" % role) + + op = opcodes.OpSetNodeParams(node_name=node_name, + master_candidate=candidate, + offline=offline, + drained=drained, + force=bool(self.useForce())) + + return baserlib.SubmitJob([op]) + + class R_2_instances(baserlib.R_Generic): """/2/instances resource.