Commit 4a90bd4f authored by Michele Tartara's avatar Michele Tartara
Browse files

Status change reason support for Reboot



Add support to the Reboot command for specifying the reason for the last
status change.

Some features are implemented as functions, even if used only once, because
they will be used by the future patches introducing reason support for all
the others commands able to alter the state of instances.
Signed-off-by: default avatarMichele Tartara <mtartara@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent a59d5fa1
......@@ -1451,7 +1451,7 @@ def InstanceShutdown(instance, timeout):
_RemoveBlockDevLinks(iname, instance.disks)
def InstanceReboot(instance, reboot_type, shutdown_timeout):
def InstanceReboot(instance, reboot_type, shutdown_timeout, reason):
"""Reboot an instance.
@type instance: L{objects.Instance}
......@@ -1481,12 +1481,15 @@ def InstanceReboot(instance, reboot_type, shutdown_timeout):
if reboot_type == constants.INSTANCE_REBOOT_SOFT:
try:
hyper.RebootInstance(instance)
reason.Store(instance.name)
except errors.HypervisorError, err:
_Fail("Failed to soft reboot instance %s: %s", instance.name, err)
elif reboot_type == constants.INSTANCE_REBOOT_HARD:
try:
InstanceShutdown(instance, shutdown_timeout)
return StartInstance(instance, False)
result = StartInstance(instance, False)
reason.Store(instance.name)
return result
except errors.HypervisorError, err:
_Fail("Failed to hard reboot instance %s: %s", instance.name, err)
else:
......
......@@ -1552,7 +1552,8 @@ commands = {
[m_force_multi, REBOOT_TYPE_OPT, IGNORE_SECONDARIES_OPT, m_node_opt,
m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT,
m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
m_inst_tags_opt, SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
m_inst_tags_opt, SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT,
REASON_OPT],
"<instance>", "Reboots an instance"),
"activate-disks": (
ActivateDisks, ARGS_ONE_INSTANCE,
......
......@@ -7361,6 +7361,7 @@ class LUInstanceReboot(LogicalUnit):
instance = self.instance
ignore_secondaries = self.op.ignore_secondaries
reboot_type = self.op.reboot_type
reason = self.op.reason
 
remote_info = self.rpc.call_instance_info(instance.primary_node,
instance.name,
......@@ -7376,7 +7377,8 @@ class LUInstanceReboot(LogicalUnit):
self.cfg.SetDiskID(disk, node_current)
result = self.rpc.call_instance_reboot(node_current, instance,
reboot_type,
self.op.shutdown_timeout)
self.op.shutdown_timeout,
reason)
result.Raise("Could not reboot instance")
else:
if instance_running:
......
......@@ -2343,5 +2343,8 @@ INSTANCE_REASON_SOURCES = compat.UniqueFrozenset([
INSTANCE_REASON_SOURCE_UNKNOWN,
])
# The default reasons for the change of state of an instance
INSTANCE_REASON_REBOOT = "reboot"
# Do not re-export imported modules
del re, _vcsversion, _autoconf, socket, pathutils, compat
......@@ -1451,6 +1451,15 @@ class OpInstanceReboot(OpCode):
"Whether to start the instance even if secondary disks are failing"),
("reboot_type", ht.NoDefault, ht.TElemOf(constants.REBOOT_TYPES),
"How to reboot instance"),
("reason", (constants.INSTANCE_REASON_SOURCE_UNKNOWN, None),
ht.TAnd(ht.TOr(ht.TList, ht.TTuple),
ht.TIsLength(2),
ht.TItems([
ht.TElemOf(constants.INSTANCE_REASON_SOURCES),
ht.TMaybeString,
])
),
"The reason why the reboot is happening"),
]
OP_RESULT = ht.TNone
......
......@@ -1001,11 +1001,11 @@ class GanetiRapiClient(object): # pylint: disable=R0904
(GANETI_RAPI_VERSION, instance)), query, None)
def RebootInstance(self, instance, reboot_type=None, ignore_secondaries=None,
dry_run=False):
dry_run=False, reason_text=None):
"""Reboots an instance.
@type instance: str
@param instance: instance to rebot
@param instance: instance to reboot
@type reboot_type: str
@param reboot_type: one of: hard, soft, full
@type ignore_secondaries: bool
......@@ -1013,6 +1013,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904
while re-assembling disks (in hard-reboot mode only)
@type dry_run: bool
@param dry_run: whether to perform a dry run
@type reason_text: string
@param reason_text: the reason for the reboot
@rtype: string
@return: job id
......@@ -1022,6 +1024,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
_AppendIf(query, reboot_type, ("type", reboot_type))
_AppendIf(query, ignore_secondaries is not None,
("ignore_secondaries", ignore_secondaries))
_AppendIf(query, reason_text, ("reason_text", reason_text))
return self._SendRequest(HTTP_POST,
("/%s/instances/%s/reboot" %
......
......@@ -1040,6 +1040,11 @@ class R_2_instances_name_reboot(baserlib.OpcodeResource):
self.queryargs.get("type", [constants.INSTANCE_REBOOT_HARD])[0],
"ignore_secondaries": bool(self._checkIntVariable("ignore_secondaries")),
"dry_run": self.dryRun(),
"reason": (
constants.INSTANCE_REASON_SOURCE_RAPI,
self._checkStringVariable("reason_text",
default=constants.INSTANCE_REASON_REBOOT),
)
})
......
......@@ -230,6 +230,7 @@ _INSTANCE_CALLS = [
("inst", ED_INST_DICT, "Instance object"),
("reboot_type", None, None),
("shutdown_timeout", None, None),
("reason_text", None, "Reason for the reboot"),
], None, None, "Returns the list of running instances on the given nodes"),
("instance_shutdown", SINGLE, None, constants.RPC_TMO_NORMAL, [
("instance", ED_INST_DICT, "Instance object"),
......
......@@ -112,6 +112,21 @@ def _DecodeImportExportIO(ieio, ieioargs):
return ieioargs
def _DefaultAlternative(value, default):
"""Returns the given value, unless it is None. In that case, returns a
default alternative.
@param value: The value to return if it is not None.
@param default: The value to return as a default alternative.
@return: The given value or the default alternative.\
"""
if value:
return value
return default
class MlockallRequestExecutor(http.server.HttpServerRequestExecutor):
"""Subclass ensuring request handlers are locked in RAM.
......@@ -633,7 +648,12 @@ class NodeRequestHandler(http.server.HttpServerHandler):
instance = objects.Instance.FromDict(params[0])
reboot_type = params[1]
shutdown_timeout = params[2]
return backend.InstanceReboot(instance, reboot_type, shutdown_timeout)
(reason_source, reason_text) = params[3]
reason_text = _DefaultAlternative(reason_text,
constants.INSTANCE_REASON_REBOOT)
reason = backend.InstReason(reason_source, reason_text)
return backend.InstanceReboot(instance, reboot_type, shutdown_timeout,
reason)
@staticmethod
def perspective_instance_balloon_memory(params):
......
......@@ -344,6 +344,7 @@ $(genOpCode "OpCode"
, pShutdownTimeout
, pIgnoreSecondaries
, pRebootType
, pReason
])
, ("OpInstanceMove",
[ pInstanceName
......
......@@ -241,7 +241,7 @@ instance Arbitrary OpCodes.OpCode where
arbitrary <*> arbitrary
"OP_INSTANCE_REBOOT" ->
OpCodes.OpInstanceReboot <$> genFQDN <*> arbitrary <*>
arbitrary <*> arbitrary
arbitrary <*> arbitrary <*> ((,) <$> arbitrary <*> genStringNE)
"OP_INSTANCE_MOVE" ->
OpCodes.OpInstanceMove <$> genFQDN <*> arbitrary <*> arbitrary <*>
genNodeNameNE <*> arbitrary
......@@ -397,6 +397,10 @@ genMacPrefix = do
octets <- vectorOf 3 $ choose (0::Int, 255)
mkNonEmpty . intercalate ":" $ map (printf "%02x") octets
-- | Generate a non empty string
genStringNE :: Gen NonEmptyString
genStringNE = genName >>= mkNonEmpty
-- | Arbitrary instance for MetaOpCode, defined here due to TH ordering.
$(genArbitrary ''OpCodes.MetaOpCode)
......
......@@ -592,6 +592,19 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertQuery("tag", ["awesome"])
def testRebootInstance(self):
self.rapi.AddResponse("6146")
job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
ignore_secondaries=True, dry_run=True,
reason_text="Updates")
self.assertEqual(6146, job_id)
self.assertHandler(rlib2.R_2_instances_name_reboot)
self.assertItems(["i-bar"])
self.assertDryRun()
self.assertQuery("type", ["hard"])
self.assertQuery("ignore_secondaries", ["1"])
self.assertQuery("reason_text", ["Updates"])
def testRebootInstanceDefaultReason(self):
self.rapi.AddResponse("6146")
job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
ignore_secondaries=True, dry_run=True)
......@@ -601,6 +614,7 @@ class GanetiRapiClientTests(testutils.GanetiTestCase):
self.assertDryRun()
self.assertQuery("type", ["hard"])
self.assertQuery("ignore_secondaries", ["1"])
self.assertQuery("reason_text", None)
def testShutdownInstance(self):
self.rapi.AddResponse("1487")
......
......@@ -370,6 +370,7 @@ class TestInstanceReboot(unittest.TestCase):
handler = _CreateHandler(rlib2.R_2_instances_name_reboot, ["inst847"], {
"dry-run": ["1"],
"ignore_secondaries": ["1"],
"reason_text": ["System update"]
}, {}, clfactory)
job_id = handler.POST()
......@@ -383,6 +384,10 @@ class TestInstanceReboot(unittest.TestCase):
self.assertEqual(op.reboot_type, constants.INSTANCE_REBOOT_HARD)
self.assertTrue(op.ignore_secondaries)
self.assertTrue(op.dry_run)
self.assertEqual(op.reason,
(constants.INSTANCE_REASON_SOURCE_RAPI,
"System update",
))
self.assertRaises(IndexError, cl.GetNextSubmittedJob)
......
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