Revision a59d5fa1
b/lib/backend.py | ||
---|---|---|
110 | 110 |
""" |
111 | 111 |
|
112 | 112 |
|
113 |
def GetInstReasonFilename(instance_name): |
|
114 |
"""Path of the file containing the reason of the instance status change. |
|
115 |
|
|
116 |
@type instance_name: string |
|
117 |
@param instance_name: The name of the instance |
|
118 |
@rtype: string |
|
119 |
@return: The path of the file |
|
120 |
|
|
121 |
""" |
|
122 |
return utils.PathJoin(pathutils.INSTANCE_REASON_DIR, instance_name) |
|
123 |
|
|
124 |
|
|
125 |
class InstReason(object): |
|
126 |
"""Class representing the reason for a change of state of a VM. |
|
127 |
|
|
128 |
It is used to allow an easy serialization of the reason, so that it can be |
|
129 |
written on a file. |
|
130 |
|
|
131 |
""" |
|
132 |
def __init__(self, source, text): |
|
133 |
"""Initialize the class with all the required values. |
|
134 |
|
|
135 |
@type text: string |
|
136 |
@param text: The textual description of the reason for changing state |
|
137 |
@type source: string |
|
138 |
@param source: The source of the state change (RAPI, CLI, ...) |
|
139 |
|
|
140 |
""" |
|
141 |
self.source = source |
|
142 |
self.text = text |
|
143 |
|
|
144 |
def GetJson(self): |
|
145 |
"""Get the JSON representation of the InstReason. |
|
146 |
|
|
147 |
@rtype: string |
|
148 |
@return : The JSON representation of the object |
|
149 |
|
|
150 |
""" |
|
151 |
return serializer.DumpJson(dict(source=self.source, text=self.text)) |
|
152 |
|
|
153 |
def Store(self, instance_name): |
|
154 |
"""Serialize on a file the reason for the last state change of an instance. |
|
155 |
|
|
156 |
The exact location of the file depends on the name of the instance and on |
|
157 |
the configuration of the Ganeti cluster defined at deploy time. |
|
158 |
|
|
159 |
@type instance_name: string |
|
160 |
@param instance_name: The name of the instance |
|
161 |
@rtype: None |
|
162 |
|
|
163 |
""" |
|
164 |
filename = GetInstReasonFilename(instance_name) |
|
165 |
utils.WriteFile(filename, data=self.GetJson()) |
|
166 |
|
|
167 |
|
|
113 | 168 |
def _Fail(msg, *args, **kwargs): |
114 | 169 |
"""Log an error and the raise an RPCFail exception. |
115 | 170 |
|
b/lib/cli.py | ||
---|---|---|
165 | 165 |
"PRIORITY_OPT", |
166 | 166 |
"RAPI_CERT_OPT", |
167 | 167 |
"READD_OPT", |
168 |
"REASON_OPT", |
|
168 | 169 |
"REBOOT_TYPE_OPT", |
169 | 170 |
"REMOVE_INSTANCE_OPT", |
170 | 171 |
"REMOVE_RESERVED_IPS_OPT", |
... | ... | |
1389 | 1390 |
help=("Hide successful results and show failures" |
1390 | 1391 |
" only (determined by the exit code)")) |
1391 | 1392 |
|
1393 |
REASON_OPT = cli_option("--reason", default=None, |
|
1394 |
help="The reason for executing a VM-state-changing" |
|
1395 |
" operation") |
|
1396 |
|
|
1392 | 1397 |
|
1393 | 1398 |
def _PriorityOptionCb(option, _, value, parser): |
1394 | 1399 |
"""Callback for processing C{--priority} option. |
b/lib/client/gnt_instance.py | ||
---|---|---|
631 | 631 |
return opcodes.OpInstanceReboot(instance_name=name, |
632 | 632 |
reboot_type=opts.reboot_type, |
633 | 633 |
ignore_secondaries=opts.ignore_secondaries, |
634 |
shutdown_timeout=opts.shutdown_timeout) |
|
634 |
shutdown_timeout=opts.shutdown_timeout, |
|
635 |
reason=(constants.INSTANCE_REASON_SOURCE_CLI, |
|
636 |
opts.reason)) |
|
635 | 637 |
|
636 | 638 |
|
637 | 639 |
def _ShutdownInstance(name, opts): |
b/lib/constants.py | ||
---|---|---|
2332 | 2332 |
# The version identifier for builtin data collectors |
2333 | 2333 |
BUILTIN_DATA_COLLECTOR_VERSION = "B" |
2334 | 2334 |
|
2335 |
# The source reasons for the change of state of an instance |
|
2336 |
INSTANCE_REASON_SOURCE_CLI = "cli" |
|
2337 |
INSTANCE_REASON_SOURCE_RAPI = "rapi" |
|
2338 |
INSTANCE_REASON_SOURCE_UNKNOWN = "unknown" |
|
2339 |
|
|
2340 |
INSTANCE_REASON_SOURCES = compat.UniqueFrozenset([ |
|
2341 |
INSTANCE_REASON_SOURCE_CLI, |
|
2342 |
INSTANCE_REASON_SOURCE_RAPI, |
|
2343 |
INSTANCE_REASON_SOURCE_UNKNOWN, |
|
2344 |
]) |
|
2345 |
|
|
2335 | 2346 |
# Do not re-export imported modules |
2336 | 2347 |
del re, _vcsversion, _autoconf, socket, pathutils, compat |
b/lib/pathutils.py | ||
---|---|---|
71 | 71 |
CRYPTO_KEYS_DIR = RUN_DIR + "/crypto" |
72 | 72 |
IMPORT_EXPORT_DIR = RUN_DIR + "/import-export" |
73 | 73 |
INSTANCE_STATUS_FILE = RUN_DIR + "/instance-status" |
74 |
INSTANCE_REASON_DIR = RUN_DIR + "/instance-reason" |
|
74 | 75 |
#: User-id pool lock directory (used user IDs have a corresponding lock file in |
75 | 76 |
#: this directory) |
76 | 77 |
UIDPOOL_LOCKDIR = RUN_DIR + "/uid-pool" |
b/lib/tools/ensure_dirs.py | ||
---|---|---|
197 | 197 |
(pathutils.LOG_OS_DIR, DIR, 0750, getent.masterd_uid, getent.daemons_gid), |
198 | 198 |
(cleaner_log_dir, DIR, 0750, getent.noded_uid, getent.noded_gid), |
199 | 199 |
(master_cleaner_log_dir, DIR, 0750, getent.masterd_uid, getent.masterd_gid), |
200 |
(pathutils.INSTANCE_REASON_DIR, DIR, 0755, getent.noded_uid, |
|
201 |
getent.noded_gid), |
|
200 | 202 |
]) |
201 | 203 |
|
202 | 204 |
return paths |
b/src/Ganeti/OpParams.hs | ||
---|---|---|
236 | 236 |
, pOpPriority |
237 | 237 |
, pDependencies |
238 | 238 |
, pComment |
239 |
, pReason |
|
239 | 240 |
, dOldQuery |
240 | 241 |
, dOldQueryNoLocking |
241 | 242 |
) where |
... | ... | |
1435 | 1436 |
pComment :: Field |
1436 | 1437 |
pComment = optionalNullSerField $ stringField "comment" |
1437 | 1438 |
|
1439 |
-- | The description of the state change reason. |
|
1440 |
pReason :: Field |
|
1441 |
pReason = simpleField "reason" [t| (InstReasonSrc, NonEmptyString) |] |
|
1442 |
|
|
1438 | 1443 |
-- * Entire opcode parameter list |
1439 | 1444 |
|
1440 | 1445 |
-- | Old-style query opcode, with locking. |
b/src/Ganeti/Types.hs | ||
---|---|---|
92 | 92 |
, opStatusToRaw |
93 | 93 |
, opStatusFromRaw |
94 | 94 |
, ELogType(..) |
95 |
, InstReasonSrc(..) |
|
95 | 96 |
) where |
96 | 97 |
|
97 | 98 |
import Control.Monad (liftM) |
... | ... | |
481 | 482 |
, ("ELogJqueueTest", 'C.elogJqueueTest) |
482 | 483 |
]) |
483 | 484 |
$(THH.makeJSONInstance ''ELogType) |
485 |
|
|
486 |
-- | Type for the source of the state change of instances. |
|
487 |
$(THH.declareSADT "InstReasonSrc" |
|
488 |
[ ("IRSCli", 'C.instanceReasonSourceCli) |
|
489 |
, ("IRSRapi", 'C.instanceReasonSourceRapi) |
|
490 |
]) |
|
491 |
$(THH.makeJSONInstance ''InstReasonSrc) |
|
492 |
|
|
493 |
|
b/test/hs/Test/Ganeti/OpCodes.hs | ||
---|---|---|
69 | 69 |
|
70 | 70 |
$(genArbitrary ''DiskAccess) |
71 | 71 |
|
72 |
$(genArbitrary ''InstReasonSrc) |
|
73 |
|
|
72 | 74 |
instance Arbitrary OpCodes.DiskIndex where |
73 | 75 |
arbitrary = choose (0, C.maxDisks - 1) >>= OpCodes.mkDiskIndex |
74 | 76 |
|
b/test/py/ganeti.backend_unittest.py | ||
---|---|---|
32 | 32 |
from ganeti import backend |
33 | 33 |
from ganeti import netutils |
34 | 34 |
from ganeti import errors |
35 |
from ganeti import serializer |
|
35 | 36 |
|
36 | 37 |
import testutils |
37 | 38 |
import mocks |
... | ... | |
537 | 538 |
self._Test("inst1.example.com", idx) |
538 | 539 |
|
539 | 540 |
|
541 |
class TestInstReason(unittest.TestCase): |
|
542 |
def testGetJson(self): |
|
543 |
reason_text = "OS Update" |
|
544 |
reason_source = constants.INSTANCE_REASON_SOURCE_CLI |
|
545 |
origDict = dict(text=reason_text, source=reason_source) |
|
546 |
|
|
547 |
reason = backend.InstReason(reason_source, reason_text) |
|
548 |
json = reason.GetJson() |
|
549 |
resultDict = serializer.LoadJson(json) |
|
550 |
|
|
551 |
self.assertEqual(origDict, resultDict) |
|
552 |
|
|
553 |
|
|
540 | 554 |
if __name__ == "__main__": |
541 | 555 |
testutils.GanetiTestProgram() |
Also available in: Unified diff