X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/4c405df7d6b1c2bbf492c9aa3e19bd797fae4204..f98e1175cc2001da476916f3c13095f0663dea75:/lib/opcodes.py diff --git a/lib/opcodes.py b/lib/opcodes.py index 2ce5487..73d5cb3 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc. +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 @@ -41,7 +41,7 @@ from ganeti import constants from ganeti import errors from ganeti import ht from ganeti import objects -from ganeti import objectutils +from ganeti import outils # Common opcode attributes @@ -159,6 +159,12 @@ _PDiskParams = \ _PHvState = ("hv_state", None, ht.TMaybeDict, "Set hypervisor states") _PDiskState = ("disk_state", None, ht.TMaybeDict, "Set disk states") +#: Opportunistic locking +_POpportunisticLocking = \ + ("opportunistic_locking", False, ht.TBool, + ("Whether to employ opportunistic locking for nodes, meaning nodes" + " already locked by another opcode won't be considered for instance" + " allocation (only when an iallocator is used)")) _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool, "Whether to ignore ipolicy violations") @@ -167,10 +173,17 @@ _PIgnoreIpolicy = ("ignore_ipolicy", False, ht.TBool, _PAllowRuntimeChgs = ("allow_runtime_changes", True, ht.TBool, "Allow runtime changes (eg. memory ballooning)") +#: IAllocator field builder +_PIAllocFromDesc = lambda desc: ("iallocator", None, ht.TMaybeString, desc) + #: a required network name _PNetworkName = ("network_name", ht.NoDefault, ht.TNonEmptyString, "Set network name") +_PTargetGroups = \ + ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), + "Destination group names or UUIDs (defaults to \"all but current group\")") + #: OP_ID conversion regular expression _OPID_RE = re.compile("([a-z])([A-Z])") @@ -198,10 +211,12 @@ _TSetParamsResult = \ ht.TListOf(ht.TAnd(ht.TIsLength(len(_TSetParamsResultItemItems)), ht.TItems(_TSetParamsResultItemItems))) -# TODO: Generate check from constants.IDISK_PARAMS_TYPES (however, not all users -# of this check support all parameters) +# In the disks option we can provide arbitrary parameters too, which +# we may not be able to validate at this level, so we just check the +# format of the dict here and the checks concerning IDISK_PARAMS will +# happen at the LU level _TDiskParams = \ - ht.Comment("Disk parameters")(ht.TDictOf(ht.TElemOf(constants.IDISK_PARAMS), + ht.Comment("Disk parameters")(ht.TDictOf(ht.TNonEmptyString, ht.TOr(ht.TNonEmptyString, ht.TInt))) _TQueryRow = \ @@ -346,8 +361,6 @@ def _CheckStorageType(storage_type): _PStorageType = ("storage_type", ht.NoDefault, _CheckStorageType, "Storage type") -_CheckNetworkType = ht.TElemOf(constants.NETWORK_VALID_TYPES) - @ht.WithDesc("IPv4 network") def _CheckCIDRNetNotation(value): @@ -404,7 +417,7 @@ _TIpNetwork6 = ht.TAnd(ht.TString, _CheckCIDR6NetNotation) _TMaybeAddr4List = ht.TMaybe(ht.TListOf(_TIpAddress4)) -class _AutoOpParamSlots(objectutils.AutoSlots): +class _AutoOpParamSlots(outils.AutoSlots): """Meta class for opcode definitions. """ @@ -423,10 +436,14 @@ class _AutoOpParamSlots(objectutils.AutoSlots): 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 + assert ("OP_DSC_FORMATTER" not in attrs or + callable(attrs["OP_DSC_FORMATTER"])), \ + ("Class '%s' uses non-callable in OP_DSC_FORMATTER (%s)" % + (name, type(attrs["OP_DSC_FORMATTER"]))) attrs["OP_ID"] = _NameToId(name) - return objectutils.AutoSlots.__new__(mcs, name, bases, attrs) + return outils.AutoSlots.__new__(mcs, name, bases, attrs) @classmethod def _GetSlots(mcs, attrs): @@ -440,7 +457,7 @@ class _AutoOpParamSlots(objectutils.AutoSlots): return [pname for (pname, _, _, _) in params] -class BaseOpCode(objectutils.ValidatedSlots): +class BaseOpCode(outils.ValidatedSlots): """A simple serializable object. This object serves as a parent class for OpCode without any custom @@ -551,7 +568,8 @@ def _BuildJobDepCheck(relative): job_id = ht.TJobId job_dep = \ - ht.TAnd(ht.TIsLength(2), + ht.TAnd(ht.TOr(ht.TList, ht.TTuple), + ht.TIsLength(2), ht.TItems([job_id, ht.TListOf(ht.TElemOf(constants.JOBS_FINALIZED))])) @@ -586,6 +604,9 @@ class OpCode(BaseOpCode): @cvar OP_DSC_FIELD: The name of a field whose value will be included in the string returned by Summary(); see the docstring of that method for details). + @cvar OP_DSC_FORMATTER: A callable that should format the OP_DSC_FIELD; if + not present, then the field will be simply converted + to string @cvar OP_PARAMS: List of opcode attributes, the default values they should get if not already defined, and types they must match. @cvar OP_RESULT: Callable to verify opcode result @@ -673,7 +694,10 @@ class OpCode(BaseOpCode): field_name = getattr(self, "OP_DSC_FIELD", None) if field_name: field_value = getattr(self, field_name, None) - if isinstance(field_value, (list, tuple)): + field_formatter = getattr(self, "OP_DSC_FORMATTER", None) + if callable(field_formatter): + field_value = field_formatter(field_value) + elif isinstance(field_value, (list, tuple)): field_value = ",".join(str(i) for i in field_value) txt = "%s(%s)" % (txt, field_value) return txt @@ -1196,8 +1220,8 @@ class OpNodeMigrate(OpCode): _PMigrationTargetNode, _PAllowRuntimeChgs, _PIgnoreIpolicy, - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PIAllocFromDesc("Iallocator for deciding the target node" + " for shared-storage instances"), ] OP_RESULT = TJobIdListOnly @@ -1209,7 +1233,7 @@ class OpNodeEvacuate(OpCode): _PEarlyRelease, _PNodeName, ("remote_node", None, ht.TMaybeString, "New secondary node"), - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), + _PIAllocFromDesc("Iallocator for computing solution"), ("mode", ht.NoDefault, ht.TElemOf(constants.NODE_EVAC_MODES), "Node evacuation mode"), ] @@ -1237,6 +1261,7 @@ class OpInstanceCreate(OpCode): _PWaitForSync, _PNameCheck, _PIgnoreIpolicy, + _POpportunisticLocking, ("beparams", ht.EmptyDict, ht.TDict, "Backend parameters for instance"), ("disks", ht.NoDefault, ht.TListOf(_TDiskParams), "Disk descriptions, for example ``[{\"%s\": 100}, {\"%s\": 5}]``;" @@ -1255,8 +1280,7 @@ class OpInstanceCreate(OpCode): ("hvparams", ht.EmptyDict, ht.TDict, "Hypervisor parameters for instance, hypervisor-dependent"), ("hypervisor", None, ht.TMaybeString, "Hypervisor"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding which node(s) to use"), + _PIAllocFromDesc("Iallocator for deciding which node(s) to use"), ("identify_defaults", False, ht.TBool, "Reset instance parameters to default if equal"), ("ip_check", True, ht.TBool, _PIpCheckDoc), @@ -1297,8 +1321,8 @@ class OpInstanceMultiAlloc(OpCode): """ OP_PARAMS = [ - ("iallocator", None, ht.TMaybeString, - "Iallocator used to allocate all the instances"), + _POpportunisticLocking, + _PIAllocFromDesc("Iallocator used to allocate all the instances"), ("instances", ht.EmptyList, ht.TListOf(ht.TInstanceOf(OpInstanceCreate)), "List of instance create opcodes describing the instances to allocate"), ] @@ -1408,6 +1432,7 @@ class OpInstanceShutdown(OpCode): OP_DSC_FIELD = "instance_name" OP_PARAMS = [ _PInstanceName, + _PForce, _PIgnoreOfflineNodes, ("timeout", constants.DEFAULT_SHUTDOWN_TIMEOUT, ht.TNonNegativeInt, "How long to wait for instance to shut down"), @@ -1442,8 +1467,7 @@ class OpInstanceReplaceDisks(OpCode): ("disks", ht.EmptyList, ht.TListOf(ht.TNonNegativeInt), "Disk indexes"), ("remote_node", None, ht.TMaybeString, "New secondary node"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding new secondary node"), + _PIAllocFromDesc("Iallocator for deciding new secondary node"), ] OP_RESULT = ht.TNone @@ -1457,8 +1481,8 @@ class OpInstanceFailover(OpCode): _PIgnoreConsistency, _PMigrationTargetNode, _PIgnoreIpolicy, - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PIAllocFromDesc("Iallocator for deciding the target node for" + " shared-storage instances"), ] OP_RESULT = ht.TNone @@ -1483,8 +1507,8 @@ class OpInstanceMigrate(OpCode): _PIgnoreIpolicy, ("cleanup", False, ht.TBool, "Whether a previously failed migration should be cleaned up"), - ("iallocator", None, ht.TMaybeString, - "Iallocator for deciding the target node for shared-storage instances"), + _PIAllocFromDesc("Iallocator for deciding the target node for" + " shared-storage instances"), ("allow_failover", False, ht.TBool, "Whether we can fallback to failover if migration is not possible"), ] @@ -1561,8 +1585,7 @@ 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"), + _PIAllocFromDesc("Iallocator for deciding new nodes"), ] OP_RESULT = ht.TNone @@ -1680,9 +1703,8 @@ class OpInstanceChangeGroup(OpCode): OP_PARAMS = [ _PInstanceName, _PEarlyRelease, - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), - ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), - "Destination group names or UUIDs (defaults to \"all but current group\""), + _PIAllocFromDesc("Iallocator for computing solution"), + _PTargetGroups, ] OP_RESULT = TJobIdListOnly @@ -1766,9 +1788,8 @@ class OpGroupEvacuate(OpCode): OP_PARAMS = [ _PGroupName, _PEarlyRelease, - ("iallocator", None, ht.TMaybeString, "Iallocator for computing solution"), - ("target_groups", None, ht.TMaybeListOf(ht.TNonEmptyString), - "Destination group names or UUIDs"), + _PIAllocFromDesc("Iallocator for computing solution"), + _PTargetGroups, ] OP_RESULT = TJobIdListOnly @@ -1784,6 +1805,17 @@ class OpOsDiagnose(OpCode): OP_RESULT = _TOldQueryResult +# ExtStorage opcodes +class OpExtStorageDiagnose(OpCode): + """Compute the list of external storage providers.""" + OP_PARAMS = [ + _POutputFields, + ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), + "Which ExtStorage Provider to diagnose"), + ] + OP_RESULT = _TOldQueryResult + + # Exports opcodes class OpBackupQuery(OpCode): """Compute the list of exported images.""" @@ -1816,11 +1848,12 @@ class OpBackupPrepare(OpCode): class OpBackupExport(OpCode): """Export an instance. - For local exports, the export destination is the node name. For remote - exports, the export destination is a list of tuples, each consisting of - hostname/IP address, port, HMAC and HMAC salt. The HMAC is calculated using - the cluster domain secret over the value "${index}:${hostname}:${port}". The - destination X509 CA must be a signed certificate. + For local exports, the export destination is the node name. For + remote exports, the export destination is a list of tuples, each + consisting of hostname/IP address, port, magic, HMAC and HMAC + salt. The HMAC is calculated using the cluster domain secret over + the value "${index}:${hostname}:${port}". The destination X509 CA + must be a signed certificate. @ivar mode: Export mode (one of L{constants.EXPORT_MODES}) @ivar target_node: Export destination @@ -1947,6 +1980,16 @@ class OpTestDelay(OpCode): ("repeat", 0, ht.TNonNegativeInt, None), ] + def OP_DSC_FORMATTER(self, value): # pylint: disable=C0103,R0201 + """Custom formatter for duration. + + """ + try: + v = float(value) + except TypeError: + v = value + return str(v) + class OpTestAllocator(OpCode): """Allocator framework testing. @@ -1959,7 +2002,7 @@ class OpTestAllocator(OpCode): return the allocator output (direction 'out') """ - OP_DSC_FIELD = "allocator" + OP_DSC_FIELD = "iallocator" OP_PARAMS = [ ("direction", ht.NoDefault, ht.TElemOf(constants.VALID_IALLOCATOR_DIRECTIONS), None), @@ -1973,7 +2016,7 @@ class OpTestAllocator(OpCode): None), ("disks", ht.NoDefault, ht.TMaybe(ht.TList), None), ("hypervisor", None, ht.TMaybeString, None), - ("allocator", None, ht.TMaybeString, None), + _PIAllocFromDesc(None), ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), None), ("memory", None, ht.TMaybe(ht.TNonNegativeInt), None), ("vcpus", None, ht.TMaybe(ht.TNonNegativeInt), None), @@ -2020,7 +2063,6 @@ class OpNetworkAdd(OpCode): OP_DSC_FIELD = "network_name" OP_PARAMS = [ _PNetworkName, - ("network_type", None, ht.TMaybe(_CheckNetworkType), "Network type"), ("network", ht.NoDefault, _TIpNetwork4, "IPv4 subnet"), ("gateway", None, ht.TMaybe(_TIpAddress4), "IPv4 gateway"), ("network6", None, ht.TMaybe(_TIpNetwork6), "IPv6 subnet"), @@ -2029,6 +2071,8 @@ class OpNetworkAdd(OpCode): "MAC address prefix that overrides cluster one"), ("add_reserved_ips", None, _TMaybeAddr4List, "Which IP addresses to reserve"), + ("conflicts_check", True, ht.TBool, + "Whether to check for conflicting IP addresses"), ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"), ] OP_RESULT = ht.TNone @@ -2052,8 +2096,6 @@ class OpNetworkSetParams(OpCode): OP_DSC_FIELD = "network_name" OP_PARAMS = [ _PNetworkName, - ("network_type", None, ht.TMaybeValueNone(_CheckNetworkType), - "Network type"), ("gateway", None, ht.TMaybeValueNone(_TIpAddress4), "IPv4 gateway"), ("network6", None, ht.TMaybeValueNone(_TIpNetwork6), "IPv6 subnet"), ("gateway6", None, ht.TMaybeValueNone(_TIpAddress6), "IPv6 gateway"), @@ -2079,7 +2121,8 @@ class OpNetworkConnect(OpCode): OP_PARAMS = [ _PGroupName, _PNetworkName, - ("network_mode", ht.NoDefault, ht.TString, "Connectivity mode"), + ("network_mode", ht.NoDefault, ht.TElemOf(constants.NIC_VALID_MODES), + "Connectivity mode"), ("network_link", ht.NoDefault, ht.TString, "Connectivity link"), ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"), ] @@ -2095,7 +2138,6 @@ class OpNetworkDisconnect(OpCode): OP_PARAMS = [ _PGroupName, _PNetworkName, - ("conflicts_check", True, ht.TBool, "Whether to check for conflicting IPs"), ] OP_RESULT = ht.TNone @@ -2104,6 +2146,7 @@ class OpNetworkQuery(OpCode): """Compute the list of networks.""" OP_PARAMS = [ _POutputFields, + _PUseLocking, ("names", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Empty list to query all groups, group names otherwise"), ]