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