Commit a59d5fa1 authored by Michele Tartara's avatar Michele Tartara
Browse files

Infrastructure for specifying instance status change reason



This patch introduces some infrastructural modifications that will be used by
the following commits to implement the support for specifying the reason for
the last status change of an instance.
Signed-off-by: default avatarMichele Tartara <mtartara@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent 834bea99
......@@ -110,6 +110,61 @@ class RPCFail(Exception):
"""
def GetInstReasonFilename(instance_name):
"""Path of the file containing the reason of the instance status change.
@type instance_name: string
@param instance_name: The name of the instance
@rtype: string
@return: The path of the file
"""
return utils.PathJoin(pathutils.INSTANCE_REASON_DIR, instance_name)
class InstReason(object):
"""Class representing the reason for a change of state of a VM.
It is used to allow an easy serialization of the reason, so that it can be
written on a file.
"""
def __init__(self, source, text):
"""Initialize the class with all the required values.
@type text: string
@param text: The textual description of the reason for changing state
@type source: string
@param source: The source of the state change (RAPI, CLI, ...)
"""
self.source = source
self.text = text
def GetJson(self):
"""Get the JSON representation of the InstReason.
@rtype: string
@return : The JSON representation of the object
"""
return serializer.DumpJson(dict(source=self.source, text=self.text))
def Store(self, instance_name):
"""Serialize on a file the reason for the last state change of an instance.
The exact location of the file depends on the name of the instance and on
the configuration of the Ganeti cluster defined at deploy time.
@type instance_name: string
@param instance_name: The name of the instance
@rtype: None
"""
filename = GetInstReasonFilename(instance_name)
utils.WriteFile(filename, data=self.GetJson())
def _Fail(msg, *args, **kwargs):
"""Log an error and the raise an RPCFail exception.
......
......@@ -165,6 +165,7 @@ __all__ = [
"PRIORITY_OPT",
"RAPI_CERT_OPT",
"READD_OPT",
"REASON_OPT",
"REBOOT_TYPE_OPT",
"REMOVE_INSTANCE_OPT",
"REMOVE_RESERVED_IPS_OPT",
......@@ -1389,6 +1390,10 @@ FAILURE_ONLY_OPT = cli_option("--failure-only", default=False,
help=("Hide successful results and show failures"
" only (determined by the exit code)"))
REASON_OPT = cli_option("--reason", default=None,
help="The reason for executing a VM-state-changing"
" operation")
def _PriorityOptionCb(option, _, value, parser):
"""Callback for processing C{--priority} option.
......
......@@ -631,7 +631,9 @@ def _RebootInstance(name, opts):
return opcodes.OpInstanceReboot(instance_name=name,
reboot_type=opts.reboot_type,
ignore_secondaries=opts.ignore_secondaries,
shutdown_timeout=opts.shutdown_timeout)
shutdown_timeout=opts.shutdown_timeout,
reason=(constants.INSTANCE_REASON_SOURCE_CLI,
opts.reason))
def _ShutdownInstance(name, opts):
......
......@@ -2332,5 +2332,16 @@ AUTO_REPAIR_ALL_RESULTS = frozenset([
# The version identifier for builtin data collectors
BUILTIN_DATA_COLLECTOR_VERSION = "B"
# The source reasons for the change of state of an instance
INSTANCE_REASON_SOURCE_CLI = "cli"
INSTANCE_REASON_SOURCE_RAPI = "rapi"
INSTANCE_REASON_SOURCE_UNKNOWN = "unknown"
INSTANCE_REASON_SOURCES = compat.UniqueFrozenset([
INSTANCE_REASON_SOURCE_CLI,
INSTANCE_REASON_SOURCE_RAPI,
INSTANCE_REASON_SOURCE_UNKNOWN,
])
# Do not re-export imported modules
del re, _vcsversion, _autoconf, socket, pathutils, compat
......@@ -71,6 +71,7 @@ SOCKET_DIR = RUN_DIR + "/socket"
CRYPTO_KEYS_DIR = RUN_DIR + "/crypto"
IMPORT_EXPORT_DIR = RUN_DIR + "/import-export"
INSTANCE_STATUS_FILE = RUN_DIR + "/instance-status"
INSTANCE_REASON_DIR = RUN_DIR + "/instance-reason"
#: User-id pool lock directory (used user IDs have a corresponding lock file in
#: this directory)
UIDPOOL_LOCKDIR = RUN_DIR + "/uid-pool"
......
......@@ -197,6 +197,8 @@ def GetPaths():
(pathutils.LOG_OS_DIR, DIR, 0750, getent.masterd_uid, getent.daemons_gid),
(cleaner_log_dir, DIR, 0750, getent.noded_uid, getent.noded_gid),
(master_cleaner_log_dir, DIR, 0750, getent.masterd_uid, getent.masterd_gid),
(pathutils.INSTANCE_REASON_DIR, DIR, 0755, getent.noded_uid,
getent.noded_gid),
])
return paths
......
......@@ -236,6 +236,7 @@ module Ganeti.OpParams
, pOpPriority
, pDependencies
, pComment
, pReason
, dOldQuery
, dOldQueryNoLocking
) where
......@@ -1435,6 +1436,10 @@ pDependencies =
pComment :: Field
pComment = optionalNullSerField $ stringField "comment"
-- | The description of the state change reason.
pReason :: Field
pReason = simpleField "reason" [t| (InstReasonSrc, NonEmptyString) |]
-- * Entire opcode parameter list
-- | Old-style query opcode, with locking.
......
......@@ -92,6 +92,7 @@ module Ganeti.Types
, opStatusToRaw
, opStatusFromRaw
, ELogType(..)
, InstReasonSrc(..)
) where
import Control.Monad (liftM)
......@@ -481,3 +482,12 @@ $(THH.declareSADT "ELogType"
, ("ELogJqueueTest", 'C.elogJqueueTest)
])
$(THH.makeJSONInstance ''ELogType)
-- | Type for the source of the state change of instances.
$(THH.declareSADT "InstReasonSrc"
[ ("IRSCli", 'C.instanceReasonSourceCli)
, ("IRSRapi", 'C.instanceReasonSourceRapi)
])
$(THH.makeJSONInstance ''InstReasonSrc)
......@@ -69,6 +69,8 @@ $(genArbitrary ''OpCodes.ReplaceDisksMode)
$(genArbitrary ''DiskAccess)
$(genArbitrary ''InstReasonSrc)
instance Arbitrary OpCodes.DiskIndex where
arbitrary = choose (0, C.maxDisks - 1) >>= OpCodes.mkDiskIndex
......
......@@ -32,6 +32,7 @@ from ganeti import constants
from ganeti import backend
from ganeti import netutils
from ganeti import errors
from ganeti import serializer
import testutils
import mocks
......@@ -537,5 +538,18 @@ class TestGetBlockDevSymlinkPath(unittest.TestCase):
self._Test("inst1.example.com", idx)
class TestInstReason(unittest.TestCase):
def testGetJson(self):
reason_text = "OS Update"
reason_source = constants.INSTANCE_REASON_SOURCE_CLI
origDict = dict(text=reason_text, source=reason_source)
reason = backend.InstReason(reason_source, reason_text)
json = reason.GetJson()
resultDict = serializer.LoadJson(json)
self.assertEqual(origDict, resultDict)
if __name__ == "__main__":
testutils.GanetiTestProgram()
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