Commit 682878d9 authored by Guido Trotter's avatar Guido Trotter
Browse files

Merge branch 'devel-2.5'



* devel-2.5:
  rapi.client.ModifyNode should PUT rather than POST
  Fix RAPI node modify client and server calls
  xen: changes to facilitate "xl" support (xen 4.1)
  xen: abstract instance config file naming
  Abstract xen's 'xm' command as a constant
  Fix RAPI documentation build
  rapi: Allow auto-promotion on node role change
  rapi: Add resource for modifying node
  opcodes: Add comment to *SetParams result description

Conflicts:
	lib/rapi/client.py
              - both functions stay, remove one empty line
	lib/rapi/rlib2.py
              - convert new function to 2.6 rapi style
	test/ganeti.rapi.client_unittest.py
              - both tests stay, trivial
Signed-off-by: default avatarGuido Trotter <ultrotter@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parents 6915fe26 55ef0cf6
...@@ -1337,6 +1337,28 @@ be a job id. ...@@ -1337,6 +1337,28 @@ be a job id.
It supports the bool ``force`` argument. It supports the bool ``force`` argument.
``/2/nodes/[node_name]/modify``
+++++++++++++++++++++++++++++++
Modifies the parameters of a node. Supports the following commands:
``POST``.
``POST``
~~~~~~~~
Returns a job ID.
Body parameters:
.. opcode_params:: OP_NODE_SET_PARAMS
:exclude: node_name
Job result:
.. opcode_result:: OP_NODE_SET_PARAMS
``/2/nodes/[node_name]/storage`` ``/2/nodes/[node_name]/storage``
++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++
......
...@@ -270,6 +270,7 @@ EXPORT_CONF_FILE = "config.ini" ...@@ -270,6 +270,7 @@ EXPORT_CONF_FILE = "config.ini"
XEN_BOOTLOADER = _autoconf.XEN_BOOTLOADER XEN_BOOTLOADER = _autoconf.XEN_BOOTLOADER
XEN_KERNEL = _autoconf.XEN_KERNEL XEN_KERNEL = _autoconf.XEN_KERNEL
XEN_INITRD = _autoconf.XEN_INITRD XEN_INITRD = _autoconf.XEN_INITRD
XEN_CMD = "xm"
KVM_PATH = _autoconf.KVM_PATH KVM_PATH = _autoconf.KVM_PATH
SOCAT_PATH = _autoconf.SOCAT_PATH SOCAT_PATH = _autoconf.SOCAT_PATH
......
...@@ -47,9 +47,22 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -47,9 +47,22 @@ class XenHypervisor(hv_base.BaseHypervisor):
ANCILLARY_FILES = [ ANCILLARY_FILES = [
"/etc/xen/xend-config.sxp", "/etc/xen/xend-config.sxp",
"/etc/xen/xl.conf",
"/etc/xen/scripts/vif-bridge", "/etc/xen/scripts/vif-bridge",
] ]
@staticmethod
def _ConfigFileName(instance_name):
"""Get the config file name for an instance.
@param instance_name: instance name
@type instance_name: str
@return: fully qualified path to instance config file
@rtype: str
"""
return "/etc/xen/%s" % instance_name
@classmethod @classmethod
def _WriteConfigFile(cls, instance, block_devices): def _WriteConfigFile(cls, instance, block_devices):
"""Write the Xen config file for the instance. """Write the Xen config file for the instance.
...@@ -64,7 +77,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -64,7 +77,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
This version of the function just writes the config file from static data. This version of the function just writes the config file from static data.
""" """
utils.WriteFile("/etc/xen/%s" % instance_name, data=data) utils.WriteFile(XenHypervisor._ConfigFileName(instance_name), data=data)
@staticmethod @staticmethod
def _ReadConfigFile(instance_name): def _ReadConfigFile(instance_name):
...@@ -72,7 +85,8 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -72,7 +85,8 @@ class XenHypervisor(hv_base.BaseHypervisor):
""" """
try: try:
file_content = utils.ReadFile("/etc/xen/%s" % instance_name) file_content = utils.ReadFile(
XenHypervisor._ConfigFileName(instance_name))
except EnvironmentError, err: except EnvironmentError, err:
raise errors.HypervisorError("Failed to load Xen config file: %s" % err) raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
return file_content return file_content
...@@ -82,7 +96,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -82,7 +96,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""Remove the xen configuration file. """Remove the xen configuration file.
""" """
utils.RemoveFile("/etc/xen/%s" % instance_name) utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
@classmethod @classmethod
def _CreateConfigCpus(cls, cpu_mask): def _CreateConfigCpus(cls, cpu_mask):
...@@ -121,7 +135,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -121,7 +135,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""Helper function for L{_GetXMList} to run "xm list". """Helper function for L{_GetXMList} to run "xm list".
""" """
result = utils.RunCmd(["xm", "list"]) result = utils.RunCmd([constants.XEN_CMD, "list"])
if result.failed: if result.failed:
logging.error("xm list failed (%s): %s", result.fail_reason, logging.error("xm list failed (%s): %s", result.fail_reason,
result.output) result.output)
...@@ -217,10 +231,10 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -217,10 +231,10 @@ class XenHypervisor(hv_base.BaseHypervisor):
""" """
self._WriteConfigFile(instance, block_devices) self._WriteConfigFile(instance, block_devices)
cmd = ["xm", "create"] cmd = [constants.XEN_CMD, "create"]
if startup_paused: if startup_paused:
cmd.extend(["--paused"]) cmd.extend(["-p"])
cmd.extend([instance.name]) cmd.extend([self._ConfigFileName(instance.name)])
result = utils.RunCmd(cmd) result = utils.RunCmd(cmd)
if result.failed: if result.failed:
...@@ -236,9 +250,9 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -236,9 +250,9 @@ class XenHypervisor(hv_base.BaseHypervisor):
name = instance.name name = instance.name
self._RemoveConfigFile(name) self._RemoveConfigFile(name)
if force: if force:
command = ["xm", "destroy", name] command = [constants.XEN_CMD, "destroy", name]
else: else:
command = ["xm", "shutdown", name] command = [constants.XEN_CMD, "shutdown", name]
result = utils.RunCmd(command) result = utils.RunCmd(command)
if result.failed: if result.failed:
...@@ -255,7 +269,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -255,7 +269,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
raise errors.HypervisorError("Failed to reboot instance %s," raise errors.HypervisorError("Failed to reboot instance %s,"
" not running" % instance.name) " not running" % instance.name)
result = utils.RunCmd(["xm", "reboot", instance.name]) result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
if result.failed: if result.failed:
raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" % raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
(instance.name, result.fail_reason, (instance.name, result.fail_reason,
...@@ -293,7 +307,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -293,7 +307,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
""" """
# note: in xen 3, memory has changed to total_memory # note: in xen 3, memory has changed to total_memory
result = utils.RunCmd(["xm", "info"]) result = utils.RunCmd([constants.XEN_CMD, "info"])
if result.failed: if result.failed:
logging.error("Can't run 'xm info' (%s): %s", result.fail_reason, logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
result.output) result.output)
...@@ -357,7 +371,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -357,7 +371,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
For Xen, this verifies that the xend process is running. For Xen, this verifies that the xend process is running.
""" """
result = utils.RunCmd(["xm", "info"]) result = utils.RunCmd([constants.XEN_CMD, "info"])
if result.failed: if result.failed:
return "'xm info' failed: %s, %s" % (result.fail_reason, result.output) return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
...@@ -464,7 +478,12 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -464,7 +478,12 @@ class XenHypervisor(hv_base.BaseHypervisor):
raise errors.HypervisorError("Remote host %s not listening on port" raise errors.HypervisorError("Remote host %s not listening on port"
" %s, cannot migrate" % (target, port)) " %s, cannot migrate" % (target, port))
args = ["xm", "migrate", "-p", "%d" % port] # FIXME: migrate must be upgraded for transitioning to "xl" (xen 4.1).
# -l doesn't exist anymore
# -p doesn't exist anymore
# -C config_file must be passed
# ssh must recognize the key of the target host for the migration
args = [constants.XEN_CMD, "migrate", "-p", "%d" % port]
if live: if live:
args.append("-l") args.append("-l")
args.extend([instance.name, target]) args.extend([instance.name, target])
...@@ -524,7 +543,7 @@ class XenHypervisor(hv_base.BaseHypervisor): ...@@ -524,7 +543,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
try: try:
cls.LinuxPowercycle() cls.LinuxPowercycle()
finally: finally:
utils.RunCmd(["xm", "debug", "R"]) utils.RunCmd([constants.XEN_CMD, "debug", "R"])
class XenPvmHypervisor(XenHypervisor): class XenPvmHypervisor(XenHypervisor):
...@@ -617,11 +636,12 @@ class XenPvmHypervisor(XenHypervisor): ...@@ -617,11 +636,12 @@ class XenPvmHypervisor(XenHypervisor):
# just in case it exists # just in case it exists
utils.RemoveFile("/etc/xen/auto/%s" % instance.name) utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
try: try:
utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue()) utils.WriteFile(cls._ConfigFileName(instance.name),
data=config.getvalue())
except EnvironmentError, err: except EnvironmentError, err:
raise errors.HypervisorError("Cannot write Xen instance confile" raise errors.HypervisorError("Cannot write Xen instance confile"
" file /etc/xen/%s: %s" % " file %s: %s" %
(instance.name, err)) (cls._ConfigFileName(instance.name), err))
return True return True
...@@ -763,11 +783,11 @@ class XenHvmHypervisor(XenHypervisor): ...@@ -763,11 +783,11 @@ class XenHvmHypervisor(XenHypervisor):
# just in case it exists # just in case it exists
utils.RemoveFile("/etc/xen/auto/%s" % instance.name) utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
try: try:
utils.WriteFile("/etc/xen/%s" % instance.name, utils.WriteFile(cls._ConfigFileName(instance.name),
data=config.getvalue()) data=config.getvalue())
except EnvironmentError, err: except EnvironmentError, err:
raise errors.HypervisorError("Cannot write Xen instance confile" raise errors.HypervisorError("Cannot write Xen instance confile"
" file /etc/xen/%s: %s" % " file %s: %s" %
(instance.name, err)) (cls._ConfigFileName(instance.name), err))
return True return True
...@@ -158,7 +158,7 @@ _TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS), ...@@ -158,7 +158,7 @@ _TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
_TSetParamsResultItemItems = [ _TSetParamsResultItemItems = [
ht.Comment("name of changed parameter")(ht.TNonEmptyString), ht.Comment("name of changed parameter")(ht.TNonEmptyString),
ht.TAny, ht.Comment("new value")(ht.TAny),
] ]
_TSetParamsResult = \ _TSetParamsResult = \
......
...@@ -1452,7 +1452,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ...@@ -1452,7 +1452,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("/%s/nodes/%s/role" % ("/%s/nodes/%s/role" %
(GANETI_RAPI_VERSION, node)), None, None) (GANETI_RAPI_VERSION, node)), None, None)
def SetNodeRole(self, node, role, force=False): def SetNodeRole(self, node, role, force=False, auto_promote=None):
"""Sets the role for a node. """Sets the role for a node.
@type node: str @type node: str
...@@ -1461,6 +1461,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ...@@ -1461,6 +1461,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904
@param role: the role to set for the node @param role: the role to set for the node
@type force: bool @type force: bool
@param force: whether to force the role change @param force: whether to force the role change
@type auto_promote: bool
@param auto_promote: Whether node(s) should be promoted to master candidate
if necessary
@rtype: string @rtype: string
@return: job id @return: job id
...@@ -1470,6 +1473,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ...@@ -1470,6 +1473,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("force", force), ("force", force),
] ]
if auto_promote is not None:
query.append(("auto-promote", auto_promote))
return self._SendRequest(HTTP_PUT, return self._SendRequest(HTTP_PUT,
("/%s/nodes/%s/role" % ("/%s/nodes/%s/role" %
(GANETI_RAPI_VERSION, node)), query, role) (GANETI_RAPI_VERSION, node)), query, role)
...@@ -1481,7 +1487,6 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ...@@ -1481,7 +1487,6 @@ class GanetiRapiClient(object): # pylint: disable=R0904
@param node: Node name @param node: Node name
@type force: bool @type force: bool
@param force: Whether to force the operation @param force: Whether to force the operation
@rtype: string @rtype: string
@return: job id @return: job id
...@@ -1494,6 +1499,21 @@ class GanetiRapiClient(object): # pylint: disable=R0904 ...@@ -1494,6 +1499,21 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("/%s/nodes/%s/powercycle" % ("/%s/nodes/%s/powercycle" %
(GANETI_RAPI_VERSION, node)), query, None) (GANETI_RAPI_VERSION, node)), query, None)
def ModifyNode(self, node, **kwargs):
"""Modifies a node.
More details for parameters can be found in the RAPI documentation.
@type node: string
@param node: Node name
@rtype: string
@return: job id
"""
return self._SendRequest(HTTP_PUT,
("/%s/nodes/%s/modify" %
(GANETI_RAPI_VERSION, node)), None, kwargs)
def GetNodeStorageUnits(self, node, storage_type, output_fields): def GetNodeStorageUnits(self, node, storage_type, output_fields):
"""Gets the storage units for a node. """Gets the storage units for a node.
......
...@@ -117,6 +117,8 @@ def GetHandlers(node_name_pattern, instance_name_pattern, ...@@ -117,6 +117,8 @@ def GetHandlers(node_name_pattern, instance_name_pattern,
rlib2.R_2_nodes_name_evacuate, rlib2.R_2_nodes_name_evacuate,
re.compile(r"^/2/nodes/(%s)/migrate$" % node_name_pattern): re.compile(r"^/2/nodes/(%s)/migrate$" % node_name_pattern):
rlib2.R_2_nodes_name_migrate, rlib2.R_2_nodes_name_migrate,
re.compile(r"^/2/nodes/(%s)/modify$" % node_name_pattern):
rlib2.R_2_nodes_name_modify,
re.compile(r"^/2/nodes/(%s)/storage$" % node_name_pattern): re.compile(r"^/2/nodes/(%s)/storage$" % node_name_pattern):
rlib2.R_2_nodes_name_storage, rlib2.R_2_nodes_name_storage,
re.compile(r"^/2/nodes/(%s)/storage/modify$" % node_name_pattern): re.compile(r"^/2/nodes/(%s)/storage/modify$" % node_name_pattern):
......
...@@ -468,6 +468,7 @@ class R_2_nodes_name_role(baserlib.OpcodeResource): ...@@ -468,6 +468,7 @@ class R_2_nodes_name_role(baserlib.OpcodeResource):
"offline": offline, "offline": offline,
"drained": drained, "drained": drained,
"force": self.useForce(), "force": self.useForce(),
"auto_promote": bool(self._checkIntVariable("auto-promote", default=0)),
}) })
...@@ -522,6 +523,23 @@ class R_2_nodes_name_migrate(baserlib.OpcodeResource): ...@@ -522,6 +523,23 @@ class R_2_nodes_name_migrate(baserlib.OpcodeResource):
}) })
class R_2_nodes_name_modify(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/modify resource.
"""
PUT_OPCODE = opcodes.OpNodeSetParams
def GetPutOpInput(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): class R_2_nodes_name_storage(baserlib.OpcodeResource):
"""/2/nodes/[node_name]/storage resource. """/2/nodes/[node_name]/storage resource.
......
...@@ -983,6 +983,14 @@ class GanetiRapiClientTests(testutils.GanetiTestCase): ...@@ -983,6 +983,14 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertFalse(self.rapi.GetLastRequestData()) self.assertFalse(self.rapi.GetLastRequestData())
self.assertEqual(self.rapi.CountPending(), 0) self.assertEqual(self.rapi.CountPending(), 0)
def testModifyNode(self):
self.rapi.AddResponse("3783")
job_id = self.client.ModifyNode("node16979.example.com", drained=True)
self.assertEqual(job_id, 3783)
self.assertHandler(rlib2.R_2_nodes_name_modify)
self.assertItems(["node16979.example.com"])
self.assertEqual(self.rapi.CountPending(), 0)
def testGetNodeStorageUnits(self): def testGetNodeStorageUnits(self):
self.rapi.AddResponse("42") self.rapi.AddResponse("42")
self.assertEqual(42, self.assertEqual(42,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment