# pylint: disable=E0203,W0201,R0902
# E0203: Access to member %r before its definition, since we use
-# objects.py which doesn't explicitely initialise its members
+# objects.py which doesn't explicitly initialise its members
# W0201: Attribute '%s' defined outside __init__
import ConfigParser
import re
import copy
+import logging
import time
from cStringIO import StringIO
from ganeti import errors
from ganeti import constants
from ganeti import netutils
+from ganeti import objectutils
from ganeti import utils
from socket import AF_INET
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
- "OS", "Node", "NodeGroup", "Cluster", "FillDict"]
+ "OS", "Node", "NodeGroup", "Cluster", "FillDict", "Network"]
_TIMESTAMPS = ["ctime", "mtime"]
_UUID = ["uuid"]
if ndparams is None:
ndparams = {}
+ if (constants.ND_OOB_PROGRAM in ndparams and
+ ndparams[constants.ND_OOB_PROGRAM] is None):
+ # will be reset by the line below
+ del ndparams[constants.ND_OOB_PROGRAM]
return FillDict(constants.NDC_DEFAULTS, ndparams)
])
-class ConfigObject(object):
+class ConfigObject(objectutils.ValidatedSlots):
"""A generic config object.
It has the following properties:
"""
__slots__ = []
- def __init__(self, **kwargs):
- for k, v in kwargs.iteritems():
- setattr(self, k, v)
-
def __getattr__(self, name):
- if name not in self._all_slots():
+ if name not in self.GetAllSlots():
raise AttributeError("Invalid object attribute %s.%s" %
(type(self).__name__, name))
return None
def __setstate__(self, state):
- slots = self._all_slots()
+ slots = self.GetAllSlots()
for name in state:
if name in slots:
setattr(self, name, state[name])
- @classmethod
- def _all_slots(cls):
- """Compute the list of all declared slots for a class.
+ def Validate(self):
+ """Validates the slots.
"""
- slots = []
- for parent in cls.__mro__:
- slots.extend(getattr(parent, "__slots__", []))
- return slots
-
- #: Public getter for the defined slots
- GetAllSlots = _all_slots
def ToDict(self):
"""Convert to a dict holding only standard python types.
"""
result = {}
- for name in self._all_slots():
+ for name in self.GetAllSlots():
value = getattr(self, name, None)
if value is not None:
result[name] = value
"ip",
"netmask",
"netdev",
- "ip_family"
+ "ip_family",
]
"nodes",
"nodegroups",
"instances",
+ "networks",
"serial_no",
] + _TIMESTAMPS
"""
mydict = super(ConfigData, self).ToDict()
mydict["cluster"] = mydict["cluster"].ToDict()
- for key in "nodes", "instances", "nodegroups":
+ for key in "nodes", "instances", "nodegroups", "networks":
mydict[key] = self._ContainerToDicts(mydict[key])
return mydict
obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
obj.nodegroups = cls._ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
+ obj.networks = cls._ContainerFromDicts(obj.networks, dict, Network)
return obj
def HasAnyDiskOfType(self, dev_type):
# gives a good approximation.
if self.HasAnyDiskOfType(constants.LD_DRBD8):
self.cluster.drbd_usermode_helper = constants.DEFAULT_DRBD_HELPER
+ if self.networks is None:
+ self.networks = {}
+ for network in self.networks.values():
+ network.UpgradeConfig()
class NIC(ConfigObject):
"""Config object representing a network card."""
- __slots__ = ["mac", "ip", "nicparams"]
+ __slots__ = ["mac", "ip", "network", "nicparams", "netinfo"]
@classmethod
def CheckParameterSyntax(cls, nicparams):
@raise errors.ConfigurationError: when a parameter is not valid
"""
- if (nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES and
- nicparams[constants.NIC_MODE] != constants.VALUE_AUTO):
- err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
- raise errors.ConfigurationError(err)
+ mode = nicparams[constants.NIC_MODE]
+ if (mode not in constants.NIC_VALID_MODES and
+ mode != constants.VALUE_AUTO):
+ raise errors.ConfigurationError("Invalid NIC mode '%s'" % mode)
- if (nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED and
+ if (mode == constants.NIC_MODE_BRIDGED and
not nicparams[constants.NIC_LINK]):
- err = "Missing bridged nic link"
- raise errors.ConfigurationError(err)
+ raise errors.ConfigurationError("Missing bridged NIC link")
class Disk(ConfigObject):
"""
if self.dev_type in [constants.LD_LV, constants.LD_FILE,
- constants.LD_BLOCKDEV, constants.LD_RBD]:
+ constants.LD_BLOCKDEV, constants.LD_RBD,
+ constants.LD_EXT]:
result = [node]
elif self.dev_type in constants.LDS_DRBD:
result = [self.logical_id[0], self.logical_id[1]]
"""
if self.dev_type in (constants.LD_LV, constants.LD_FILE,
- constants.LD_RBD):
+ constants.LD_RBD, constants.LD_EXT):
self.size += amount
elif self.dev_type == constants.LD_DRBD8:
if self.children:
result = list()
dt_params = disk_params[disk_template]
if disk_template == constants.DT_DRBD8:
- drbd_params = {
+ result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8], {
constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE],
constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS],
constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS],
constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET],
constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE],
constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE],
- }
-
- drbd_params = \
- FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8],
- drbd_params)
-
- result.append(drbd_params)
+ }))
# data LV
- data_params = {
+ result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
- }
- data_params = \
- FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
- data_params)
- result.append(data_params)
+ }))
# metadata LV
- meta_params = {
+ result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
- }
- meta_params = \
- FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
- meta_params)
- result.append(meta_params)
+ }))
- elif (disk_template == constants.DT_FILE or
- disk_template == constants.DT_SHARED_FILE):
+ elif disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE):
result.append(constants.DISK_LD_DEFAULTS[constants.LD_FILE])
elif disk_template == constants.DT_PLAIN:
- params = {
+ result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
constants.LDP_STRIPES: dt_params[constants.LV_STRIPES],
- }
- params = \
- FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
- params)
- result.append(params)
+ }))
elif disk_template == constants.DT_BLOCK:
result.append(constants.DISK_LD_DEFAULTS[constants.LD_BLOCKDEV])
elif disk_template == constants.DT_RBD:
- params = {
- constants.LDP_POOL: dt_params[constants.RBD_POOL]
- }
- params = \
- FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD],
- params)
- result.append(params)
+ result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD], {
+ constants.LDP_POOL: dt_params[constants.RBD_POOL],
+ }))
+
+ elif disk_template == constants.DT_EXT:
+ result.append(constants.DISK_LD_DEFAULTS[constants.LD_EXT])
return result
"""
@classmethod
- def CheckParameterSyntax(cls, ipolicy):
+ def CheckParameterSyntax(cls, ipolicy, check_std):
""" Check the instance policy for validity.
"""
for param in constants.ISPECS_PARAMETERS:
- InstancePolicy.CheckISpecSyntax(ipolicy, param)
+ InstancePolicy.CheckISpecSyntax(ipolicy, param, check_std)
if constants.IPOLICY_DTS in ipolicy:
InstancePolicy.CheckDiskTemplates(ipolicy[constants.IPOLICY_DTS])
for key in constants.IPOLICY_PARAMETERS:
utils.CommaJoin(wrong_keys))
@classmethod
- def CheckISpecSyntax(cls, ipolicy, name):
+ def CheckISpecSyntax(cls, ipolicy, name, check_std):
"""Check the instance policy for validity on a given key.
We check if the instance policy makes sense for a given key, that is
@param ipolicy: dictionary with min, max, std specs
@type name: string
@param name: what are the limits for
+ @type check_std: bool
+ @param check_std: Whether to check std value or just assume compliance
@raise errors.ConfigureError: when specs for given name are not valid
"""
min_v = ipolicy[constants.ISPECS_MIN].get(name, 0)
- std_v = ipolicy[constants.ISPECS_STD].get(name, min_v)
+
+ if check_std:
+ std_v = ipolicy[constants.ISPECS_STD].get(name, min_v)
+ std_msg = std_v
+ else:
+ std_v = min_v
+ std_msg = "-"
+
max_v = ipolicy[constants.ISPECS_MAX].get(name, std_v)
err = ("Invalid specification of min/max/std values for %s: %s/%s/%s" %
(name,
ipolicy[constants.ISPECS_MIN].get(name, "-"),
ipolicy[constants.ISPECS_MAX].get(name, "-"),
- ipolicy[constants.ISPECS_STD].get(name, "-")))
+ std_msg))
if min_v > std_v or std_v > max_v:
raise errors.ConfigurationError(err)
return tuple(all_nodes)
secondary_nodes = property(_ComputeSecondaryNodes, None, None,
- "List of secondary nodes")
+ "List of names of secondary nodes")
def _ComputeAllNodes(self):
"""Compute the list of all nodes.
return tuple(all_nodes)
all_nodes = property(_ComputeAllNodes, None, None,
- "List of all nodes of the instance")
+ "List of names of all the nodes of the instance")
def MapLVsByNode(self, lvmap=None, devs=None, node=None):
"""Provide a mapping of nodes to LVs this instance owns.
GetVolumeList()
"""
- if node == None:
+ if node is None:
node = self.primary_node
if lvmap is None:
return cls.SplitNameVariant(name)[1]
+class ExtStorage(ConfigObject):
+ """Config object representing an External Storage Provider.
+
+ """
+ __slots__ = [
+ "name",
+ "path",
+ "create_script",
+ "remove_script",
+ "grow_script",
+ "attach_script",
+ "detach_script",
+ "setinfo_script",
+ "verify_script",
+ "supported_parameters",
+ ]
+
+
class NodeHvState(ConfigObject):
"""Hypvervisor state on a node.
if self.ndparams is None:
self.ndparams = {}
+ # And remove any global parameter
+ for key in constants.NDC_GLOBALS:
+ if key in self.ndparams:
+ logging.warning("Ignoring %s node parameter for node %s",
+ key, self.name)
+ del self.ndparams[key]
if self.powered is None:
self.powered = True
"hv_state_static",
"disk_state_static",
"alloc_policy",
+ "networks",
] + _TIMESTAMPS + _UUID
def ToDict(self):
if self.ipolicy is None:
self.ipolicy = MakeEmptyIPolicy()
+ if self.networks is None:
+ self.networks = {}
+
def FillND(self, node):
"""Return filled out ndparams for L{objects.Node}
# code can be removed once upgrading straight from 2.0 is deprecated.
if self.default_hypervisor is not None:
self.enabled_hypervisors = ([self.default_hypervisor] +
- [hvname for hvname in self.enabled_hypervisors
- if hvname != self.default_hypervisor])
+ [hvname for hvname in self.enabled_hypervisors
+ if hvname != self.default_hypervisor])
self.default_hypervisor = None
# maintain_node_health added after 2.1.1
@ivar fields: List of L{QueryFieldDefinition} objects
"""
- __slots__ = [
- ]
+ __slots__ = []
class MigrationStatus(ConfigObject):
return True
+class Network(TaggableObject):
+ """Object representing a network definition for ganeti.
+
+ """
+ __slots__ = [
+ "name",
+ "serial_no",
+ "network_type",
+ "mac_prefix",
+ "family",
+ "network",
+ "network6",
+ "gateway",
+ "gateway6",
+ "size",
+ "reservations",
+ "ext_reservations",
+ ] + _TIMESTAMPS + _UUID
+
+ def HooksDict(self, prefix):
+ """Export a dictionary used by hooks with a network's information.
+
+ @type prefix: String
+ @param prefix: Prefix to prepend to the dict entries
+
+ """
+ result = {
+ "%sNETWORK" % prefix: self.name,
+ "%sNETWORK_UUID" % prefix: self.uuid,
+ "%sNETWORK_FAMILY" % prefix: str(self.family),
+ "%sNETWORK_TAGS" % prefix: " ".join(self.tags),
+ }
+ if self.network:
+ result["%sNETWORK_SUBNET" % prefix] = self.network
+ if self.gateway:
+ result["%sNETWORK_GATEWAY" % prefix] = self.gateway
+ if self.network6:
+ result["%sNETWORK_SUBNET6" % prefix] = self.network6
+ if self.gateway6:
+ result["%sNETWORK_GATEWAY6" % prefix] = self.gateway6
+ if self.mac_prefix:
+ result["%sNETWORK_MAC_PREFIX" % prefix] = self.mac_prefix
+ if self.network_type:
+ result["%sNETWORK_TYPE" % prefix] = self.network_type
+
+ return result
+
+
class SerializableConfigParser(ConfigParser.SafeConfigParser):
"""Simple wrapper over ConfigParse that allows serialization.
cfp = cls()
cfp.readfp(buf)
return cfp
+
+
+class LvmPvInfo(ConfigObject):
+ """Information about an LVM physical volume (PV).
+
+ @type name: string
+ @ivar name: name of the PV
+ @type vg_name: string
+ @ivar vg_name: name of the volume group containing the PV
+ @type size: float
+ @ivar size: size of the PV in MiB
+ @type free: float
+ @ivar free: free space in the PV, in MiB
+ @type attributes: string
+ @ivar attributes: PV attributes
+ @type lv_list: list of strings
+ @ivar lv_list: names of the LVs hosted on the PV
+ """
+ __slots__ = [
+ "name",
+ "vg_name",
+ "size",
+ "free",
+ "attributes",
+ "lv_list"
+ ]
+
+ def IsEmpty(self):
+ """Is this PV empty?
+
+ """
+ return self.size <= (self.free + 1)
+
+ def IsAllocatable(self):
+ """Is this PV allocatable?
+
+ """
+ return ("a" in self.attributes)