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.
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``
++++++++++++++++++++++++++++++++
......
......@@ -270,6 +270,7 @@ EXPORT_CONF_FILE = "config.ini"
XEN_BOOTLOADER = _autoconf.XEN_BOOTLOADER
XEN_KERNEL = _autoconf.XEN_KERNEL
XEN_INITRD = _autoconf.XEN_INITRD
XEN_CMD = "xm"
KVM_PATH = _autoconf.KVM_PATH
SOCAT_PATH = _autoconf.SOCAT_PATH
......
......@@ -47,9 +47,22 @@ class XenHypervisor(hv_base.BaseHypervisor):
ANCILLARY_FILES = [
"/etc/xen/xend-config.sxp",
"/etc/xen/xl.conf",
"/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
def _WriteConfigFile(cls, instance, block_devices):
"""Write the Xen config file for the instance.
......@@ -64,7 +77,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
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
def _ReadConfigFile(instance_name):
......@@ -72,7 +85,8 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""
try:
file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
file_content = utils.ReadFile(
XenHypervisor._ConfigFileName(instance_name))
except EnvironmentError, err:
raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
return file_content
......@@ -82,7 +96,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""Remove the xen configuration file.
"""
utils.RemoveFile("/etc/xen/%s" % instance_name)
utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
@classmethod
def _CreateConfigCpus(cls, cpu_mask):
......@@ -121,7 +135,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""Helper function for L{_GetXMList} to run "xm list".
"""
result = utils.RunCmd(["xm", "list"])
result = utils.RunCmd([constants.XEN_CMD, "list"])
if result.failed:
logging.error("xm list failed (%s): %s", result.fail_reason,
result.output)
......@@ -217,10 +231,10 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""
self._WriteConfigFile(instance, block_devices)
cmd = ["xm", "create"]
cmd = [constants.XEN_CMD, "create"]
if startup_paused:
cmd.extend(["--paused"])
cmd.extend([instance.name])
cmd.extend(["-p"])
cmd.extend([self._ConfigFileName(instance.name)])
result = utils.RunCmd(cmd)
if result.failed:
......@@ -236,9 +250,9 @@ class XenHypervisor(hv_base.BaseHypervisor):
name = instance.name
self._RemoveConfigFile(name)
if force:
command = ["xm", "destroy", name]
command = [constants.XEN_CMD, "destroy", name]
else:
command = ["xm", "shutdown", name]
command = [constants.XEN_CMD, "shutdown", name]
result = utils.RunCmd(command)
if result.failed:
......@@ -255,7 +269,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
raise errors.HypervisorError("Failed to reboot instance %s,"
" not running" % instance.name)
result = utils.RunCmd(["xm", "reboot", instance.name])
result = utils.RunCmd([constants.XEN_CMD, "reboot", instance.name])
if result.failed:
raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
(instance.name, result.fail_reason,
......@@ -293,7 +307,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
"""
# 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:
logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
result.output)
......@@ -357,7 +371,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
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:
return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
......@@ -464,7 +478,12 @@ class XenHypervisor(hv_base.BaseHypervisor):
raise errors.HypervisorError("Remote host %s not listening on 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:
args.append("-l")
args.extend([instance.name, target])
......@@ -524,7 +543,7 @@ class XenHypervisor(hv_base.BaseHypervisor):
try:
cls.LinuxPowercycle()
finally:
utils.RunCmd(["xm", "debug", "R"])
utils.RunCmd([constants.XEN_CMD, "debug", "R"])
class XenPvmHypervisor(XenHypervisor):
......@@ -617,11 +636,12 @@ class XenPvmHypervisor(XenHypervisor):
# just in case it exists
utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
try:
utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
utils.WriteFile(cls._ConfigFileName(instance.name),
data=config.getvalue())
except EnvironmentError, err:
raise errors.HypervisorError("Cannot write Xen instance confile"
" file /etc/xen/%s: %s" %
(instance.name, err))
" file %s: %s" %
(cls._ConfigFileName(instance.name), err))
return True
......@@ -763,11 +783,11 @@ class XenHvmHypervisor(XenHypervisor):
# just in case it exists
utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
try:
utils.WriteFile("/etc/xen/%s" % instance.name,
utils.WriteFile(cls._ConfigFileName(instance.name),
data=config.getvalue())
except EnvironmentError, err:
raise errors.HypervisorError("Cannot write Xen instance confile"
" file /etc/xen/%s: %s" %
(instance.name, err))
" file %s: %s" %
(cls._ConfigFileName(instance.name), err))
return True
......@@ -158,7 +158,7 @@ _TestNicDef = ht.TDictOf(ht.TElemOf(constants.INIC_PARAMS),
_TSetParamsResultItemItems = [
ht.Comment("name of changed parameter")(ht.TNonEmptyString),
ht.TAny,
ht.Comment("new value")(ht.TAny),
]
_TSetParamsResult = \
......
......@@ -1452,7 +1452,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("/%s/nodes/%s/role" %
(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.
@type node: str
......@@ -1461,6 +1461,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904
@param role: the role to set for the node
@type force: bool
@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
@return: job id
......@@ -1470,6 +1473,9 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("force", force),
]
if auto_promote is not None:
query.append(("auto-promote", auto_promote))
return self._SendRequest(HTTP_PUT,
("/%s/nodes/%s/role" %
(GANETI_RAPI_VERSION, node)), query, role)
......@@ -1481,7 +1487,6 @@ class GanetiRapiClient(object): # pylint: disable=R0904
@param node: Node name
@type force: bool
@param force: Whether to force the operation
@rtype: string
@return: job id
......@@ -1494,6 +1499,21 @@ class GanetiRapiClient(object): # pylint: disable=R0904
("/%s/nodes/%s/powercycle" %
(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):
"""Gets the storage units for a node.
......
......@@ -117,6 +117,8 @@ def GetHandlers(node_name_pattern, instance_name_pattern,
rlib2.R_2_nodes_name_evacuate,
re.compile(r"^/2/nodes/(%s)/migrate$" % node_name_pattern):
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):
rlib2.R_2_nodes_name_storage,
re.compile(r"^/2/nodes/(%s)/storage/modify$" % node_name_pattern):
......
......@@ -468,6 +468,7 @@ class R_2_nodes_name_role(baserlib.OpcodeResource):
"offline": offline,
"drained": drained,
"force": self.useForce(),
"auto_promote": bool(self._checkIntVariable("auto-promote", default=0)),
})
......@@ -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):
"""/2/nodes/[node_name]/storage resource.
......
......@@ -983,6 +983,14 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertFalse(self.rapi.GetLastRequestData())
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):
self.rapi.AddResponse("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