X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/487966732137692696f5f00fe7504e8140d6b268..5227de569fa4b0dc54c0a9823ba81ad162e476ff:/lib/opcodes.py diff --git a/lib/opcodes.py b/lib/opcodes.py index f805b56..8a88bf0 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ from ganeti import constants from ganeti import errors from ganeti import ht from ganeti import objects -from ganeti import query +from ganeti import objectutils # Common opcode attributes @@ -81,10 +81,12 @@ _PMigrationLive = ("live", None, ht.TMaybeBool, "Legacy setting for live migration, do not use") #: Tag type -_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), None) +_PTagKind = ("kind", ht.NoDefault, ht.TElemOf(constants.VALID_TAG_TYPES), + "Tag kind") #: List of tag strings -_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), None) +_PTags = ("tags", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), + "List of tag names") _PForceVariant = ("force_variant", False, ht.TBool, "Whether to force an unknown OS variant") @@ -92,6 +94,10 @@ _PForceVariant = ("force_variant", False, ht.TBool, _PWaitForSync = ("wait_for_sync", True, ht.TBool, "Whether to wait for the disk to synchronize") +_PWaitForSyncFalse = ("wait_for_sync", False, ht.TBool, + "Whether to wait for the disk to synchronize" + " (defaults to false)") + _PIgnoreConsistency = ("ignore_consistency", False, ht.TBool, "Whether to ignore disk consistency") @@ -255,13 +261,13 @@ def _GenerateObjectTypeCheck(obj, fields_types): return ht.TStrictDict(True, True, fields_types) -_TObjFdefs = \ - _GenerateObjectTypeCheck(objects.QueryFieldDefinition, { - "name": ht.TRegex(query.FIELD_NAME_RE), - "title": ht.TRegex(query.TITLE_RE), - "kind": ht.TElemOf(constants.QFT_ALL), - "doc": ht.TRegex(query.DOC_RE), - }) +_TQueryFieldDef = \ + _GenerateObjectTypeCheck(objects.QueryFieldDefinition, { + "name": ht.TNonEmptyString, + "title": ht.TNonEmptyString, + "kind": ht.TElemOf(constants.QFT_ALL), + "doc": ht.TNonEmptyString, + }) def RequireFileStorage(): @@ -328,6 +334,7 @@ def _CheckStorageType(storage_type): raise errors.OpPrereqError("Unknown storage type: %s" % storage_type, errors.ECODE_INVAL) if storage_type == constants.ST_FILE: + # TODO: What about shared file storage? RequireFileStorage() return True @@ -337,7 +344,7 @@ _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType, "Storage type") -class _AutoOpParamSlots(type): +class _AutoOpParamSlots(objectutils.AutoSlots): """Meta class for opcode definitions. """ @@ -351,27 +358,29 @@ class _AutoOpParamSlots(type): @param attrs: Class attributes """ - assert "__slots__" not in attrs, \ - "Class '%s' defines __slots__ when it should use OP_PARAMS" % name assert "OP_ID" not in attrs, "Class '%s' defining OP_ID" % name + slots = mcs._GetSlots(attrs) + assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ + "Class '%s' uses unknown field in OP_DSC_FIELD" % name + attrs["OP_ID"] = _NameToId(name) + return objectutils.AutoSlots.__new__(mcs, name, bases, attrs) + + @classmethod + def _GetSlots(mcs, attrs): + """Build the slots out of OP_PARAMS. + + """ # Always set OP_PARAMS to avoid duplicates in BaseOpCode.GetAllParams params = attrs.setdefault("OP_PARAMS", []) # Use parameter names as slots - slots = [pname for (pname, _, _, _) in params] - - assert "OP_DSC_FIELD" not in attrs or attrs["OP_DSC_FIELD"] in slots, \ - "Class '%s' uses unknown field in OP_DSC_FIELD" % name + return [pname for (pname, _, _, _) in params] - attrs["__slots__"] = slots - return type.__new__(mcs, name, bases, attrs) - - -class BaseOpCode(object): +class BaseOpCode(objectutils.ValidatedSlots): """A simple serializable object. This object serves as a parent class for OpCode without any custom @@ -382,22 +391,6 @@ class BaseOpCode(object): # as OP_ID is dynamically defined __metaclass__ = _AutoOpParamSlots - def __init__(self, **kwargs): - """Constructor for BaseOpCode. - - The constructor takes only keyword arguments and will set - attributes on this object based on the passed arguments. As such, - it means that you should not pass arguments which are not in the - __slots__ attribute for this class. - - """ - slots = self._all_slots() - for key in kwargs: - if key not in slots: - raise TypeError("Object %s doesn't support the parameter '%s'" % - (self.__class__.__name__, key)) - setattr(self, key, kwargs[key]) - def __getstate__(self): """Generic serializer. @@ -409,7 +402,7 @@ class BaseOpCode(object): """ state = {} - for name in self._all_slots(): + for name in self.GetAllSlots(): if hasattr(self, name): state[name] = getattr(self, name) return state @@ -428,7 +421,7 @@ class BaseOpCode(object): raise ValueError("Invalid data to __setstate__: expected dict, got %s" % type(state)) - for name in self._all_slots(): + for name in self.GetAllSlots(): if name not in state and hasattr(self, name): delattr(self, name) @@ -436,16 +429,6 @@ class BaseOpCode(object): setattr(self, name, state[name]) @classmethod - def _all_slots(cls): - """Compute the list of all declared slots for a class. - - """ - slots = [] - for parent in cls.__mro__: - slots.extend(getattr(parent, "__slots__", [])) - return slots - - @classmethod def GetAllParams(cls): """Compute list of all parameters for an opcode. @@ -455,7 +438,7 @@ class BaseOpCode(object): slots.extend(getattr(parent, "OP_PARAMS", [])) return slots - def Validate(self, set_defaults): + def Validate(self, set_defaults): # pylint: disable=W0221 """Validate opcode parameters, optionally setting default values. @type set_defaults: bool @@ -486,8 +469,9 @@ class BaseOpCode(object): if set_defaults or hasattr(self, attr_name): attr_val = getattr(self, attr_name) if not test(attr_val): - logging.error("OpCode %s, parameter %s, has invalid type %s/value %s", - self.OP_ID, attr_name, type(attr_val), attr_val) + logging.error("OpCode %s, parameter %s, has invalid type %s/value %s" + " expecting type %s", + self.OP_ID, attr_name, type(attr_val), attr_val, test) raise errors.OpPrereqError("Parameter '%s.%s' fails validation" % (self.OP_ID, attr_name), errors.ECODE_INVAL) @@ -868,13 +852,13 @@ class OpClusterSetParams(OpCode): ("reserved_lvs", None, ht.TMaybeListOf(ht.TNonEmptyString), "List of reserved LVs"), ("hidden_os", None, _TestClusterOsList, - "Modify list of hidden operating systems. Each modification must have" - " two items, the operation and the OS name. The operation can be" - " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)), + "Modify list of hidden operating systems: each modification must have" + " two items, the operation and the OS name; the operation can be" + " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), ("blacklisted_os", None, _TestClusterOsList, - "Modify list of blacklisted operating systems. Each modification must have" - " two items, the operation and the OS name. The operation can be" - " ``%s`` or ``%s``." % (constants.DDM_ADD, constants.DDM_REMOVE)), + "Modify list of blacklisted operating systems: each modification must" + " have two items, the operation and the OS name; the operation can be" + " ``%s`` or ``%s``" % (constants.DDM_ADD, constants.DDM_REMOVE)), ("use_external_mip_script", None, ht.TMaybeBool, "Whether to use an external master IP address setup script"), ] @@ -916,14 +900,14 @@ class OpQuery(OpCode): _PUseLocking, ("fields", ht.NoDefault, ht.TListOf(ht.TNonEmptyString), "Requested fields"), - ("qfilter", None, ht.TOr(ht.TNone, ht.TListOf), + ("qfilter", None, ht.TOr(ht.TNone, ht.TList), "Query filter"), ] OP_RESULT = \ - _GenerateObjectTypeCheck(objects.QueryResponse, { - "fields": ht.TListOf(_TObjFdefs), - "data": _TQueryResult, - }) + _GenerateObjectTypeCheck(objects.QueryResponse, { + "fields": ht.TListOf(_TQueryFieldDef), + "data": _TQueryResult, + }) class OpQueryFields(OpCode): @@ -940,9 +924,9 @@ class OpQueryFields(OpCode): "Requested fields; if not given, all are returned"), ] OP_RESULT = \ - _GenerateObjectTypeCheck(objects.QueryFieldsResponse, { - "fields": ht.TListOf(_TObjFdefs), - }) + _GenerateObjectTypeCheck(objects.QueryFieldsResponse, { + "fields": ht.TListOf(_TQueryFieldDef), + }) class OpOobCommand(OpCode): @@ -1060,6 +1044,7 @@ class OpNodeQueryStorage(OpCode): class OpNodeModifyStorage(OpCode): """Modifies the properies of a storage unit""" + OP_DSC_FIELD = "node_name" OP_PARAMS = [ _PNodeName, _PStorageType, @@ -1225,6 +1210,66 @@ class OpInstanceCreate(OpCode): OP_RESULT = ht.Comment("instance nodes")(ht.TListOf(ht.TNonEmptyString)) +class OpInstanceMultiAlloc(OpCode): + """Allocates multiple instances. + + """ + OP_PARAMS = [ + ("iallocator", None, ht.TMaybeString, + "Iallocator used to allocate all the instances"), + ("instances", [], ht.TListOf(ht.TInstanceOf(OpInstanceCreate)), + "List of instance create opcodes describing the instances to allocate"), + ] + _JOB_LIST = ht.Comment("List of submitted jobs")(TJobIdList) + ALLOCATABLE_KEY = "allocatable" + FAILED_KEY = "allocatable" + OP_RESULT = ht.TStrictDict(True, True, { + constants.JOB_IDS_KEY: _JOB_LIST, + ALLOCATABLE_KEY: ht.TListOf(ht.TNonEmptyString), + FAILED_KEY: ht.TListOf(ht.TNonEmptyString) + }) + + def __getstate__(self): + """Generic serializer. + + """ + state = OpCode.__getstate__(self) + if hasattr(self, "instances"): + # pylint: disable=E1101 + state["instances"] = [inst.__getstate__() for inst in self.instances] + return state + + def __setstate__(self, state): + """Generic unserializer. + + This method just restores from the serialized state the attributes + of the current instance. + + @param state: the serialized opcode data + @type state: C{dict} + + """ + if not isinstance(state, dict): + raise ValueError("Invalid data to __setstate__: expected dict, got %s" % + type(state)) + + if "instances" in state: + insts = [OpCode.LoadOpCode(inst) for inst in state["instances"]] + state["instances"] = insts + return OpCode.__setstate__(self, state) + + def Validate(self, set_defaults): + """Validates this opcode. + + We do this recursively. + + """ + OpCode.Validate(self, set_defaults) + + for inst in self.instances: # pylint: disable=E1101 + inst.Validate(set_defaults) + + class OpInstanceReinstall(OpCode): """Reinstall an instance's OS.""" OP_DSC_FIELD = "instance_name" @@ -1400,6 +1445,7 @@ class OpInstanceActivateDisks(OpCode): OP_PARAMS = [ _PInstanceName, ("ignore_size", False, ht.TBool, "Whether to ignore recorded size"), + _PWaitForSyncFalse, ] OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(3), ht.TItems([ht.TNonEmptyString, @@ -1433,6 +1479,8 @@ class OpInstanceRecreateDisks(OpCode): " index and a possibly empty dictionary with disk parameter changes"), ("nodes", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "New instance nodes, if relocation is desired"), + ("iallocator", None, ht.TMaybeString, + "Iallocator for deciding new nodes"), ] OP_RESULT = ht.TNone @@ -1499,17 +1547,17 @@ class OpInstanceSetParams(OpCode): _PForceVariant, _PIgnoreIpolicy, ("nics", ht.EmptyList, TestNicModifications, - "List of NIC changes. Each item is of the form ``(op, index, settings)``." - " ``op`` is one of ``%s``, ``%s`` or ``%s``. ``index`` can be either -1 to" - " refer to the last position, or a zero-based index number. A deprecated" - " version of this parameter used the form ``(op, settings)``, where " - " ``op`` can be ``%s`` to add a new NIC with the specified settings," - " ``%s`` to remove the last NIC or a number to modify the settings" - " of the NIC with that index." % + "List of NIC changes: each item is of the form ``(op, index, settings)``," + " ``op`` is one of ``%s``, ``%s`` or ``%s``, ``index`` can be either -1" + " to refer to the last position, or a zero-based index number; a" + " deprecated version of this parameter used the form ``(op, settings)``," + " where ``op`` can be ``%s`` to add a new NIC with the specified" + " settings, ``%s`` to remove the last NIC or a number to modify the" + " settings of the NIC with that index" % (constants.DDM_ADD, constants.DDM_MODIFY, constants.DDM_REMOVE, constants.DDM_ADD, constants.DDM_REMOVE)), ("disks", ht.EmptyList, TestDiskModifications, - "List of disk changes. See ``nics``."), + "List of disk changes; see ``nics``"), ("beparams", ht.EmptyDict, ht.TDict, "Per-instance backend parameters"), ("runtime_mem", None, ht.TMaybeStrictPositiveInt, "New runtime memory"), ("hvparams", ht.EmptyDict, ht.TDict, @@ -1519,7 +1567,7 @@ class OpInstanceSetParams(OpCode): ("remote_node", None, ht.TMaybeString, "Secondary node (used when changing disk template)"), ("os_name", None, ht.TMaybeString, - "Change instance's OS name. Does not reinstall the instance."), + "Change the instance's OS without reinstalling the instance"), ("osparams", None, ht.TMaybeDict, "Per-instance OS parameters"), ("wait_for_sync", True, ht.TBool, "Whether to wait for the disk to synchronize, when changing template"), @@ -1535,8 +1583,10 @@ class OpInstanceGrowDisk(OpCode): _PInstanceName, _PWaitForSync, ("disk", ht.NoDefault, ht.TInt, "Disk index"), - ("amount", ht.NoDefault, ht.TInt, + ("amount", ht.NoDefault, ht.TPositiveInt, "Amount of disk space to add (megabytes)"), + ("absolute", False, ht.TBool, + "Whether the amount parameter is an absolute target or a relative one"), ] OP_RESULT = ht.TNone @@ -1738,8 +1788,11 @@ class OpTagsGet(OpCode): OP_DSC_FIELD = "name" OP_PARAMS = [ _PTagKind, + # Not using _PUseLocking as the default is different for historical reasons + ("use_locking", True, ht.TBool, "Whether to use synchronization"), # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object to retrieve tags from"), ] OP_RESULT = ht.TListOf(ht.TNonEmptyString) @@ -1748,7 +1801,8 @@ class OpTagsSearch(OpCode): """Searches the tags in the cluster for a given pattern.""" OP_DSC_FIELD = "pattern" OP_PARAMS = [ - ("pattern", ht.NoDefault, ht.TNonEmptyString, None), + ("pattern", ht.NoDefault, ht.TNonEmptyString, + "Search pattern (regular expression)"), ] OP_RESULT = ht.TListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([ ht.TNonEmptyString, @@ -1762,7 +1816,8 @@ class OpTagsSet(OpCode): _PTagKind, _PTags, # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object where tag(s) should be added"), ] OP_RESULT = ht.TNone @@ -1773,7 +1828,8 @@ class OpTagsDel(OpCode): _PTagKind, _PTags, # Name is only meaningful for nodes and instances - ("name", ht.NoDefault, ht.TMaybeString, None), + ("name", ht.NoDefault, ht.TMaybeString, + "Name of object where tag(s) should be deleted"), ] OP_RESULT = ht.TNone @@ -1844,6 +1900,8 @@ class OpTestAllocator(OpCode): ("evac_mode", None, ht.TOr(ht.TNone, ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)), None), ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), None), + ("spindle_use", 1, ht.TPositiveInt, None), + ("count", 1, ht.TPositiveInt, None), ]