X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/319856a9ed410d3dd6493c837a3781176406e9ce..785d79f724265675b86cb76959b757d553d077e5:/lib/objects.py diff --git a/lib/objects.py b/lib/objects.py index f85de16..fc3ed09 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +# # # Copyright (C) 2006, 2007 Google Inc. @@ -27,10 +27,9 @@ pass to and from external parties. """ -import simplejson -from cStringIO import StringIO import ConfigParser import re +from cStringIO import StringIO from ganeti import errors from ganeti import constants @@ -82,30 +81,6 @@ class ConfigObject(object): if name in self.__slots__: setattr(self, name, state[name]) - def Dump(self, fobj): - """Dump to a file object. - - """ - simplejson.dump(self.ToDict(), fobj) - - @classmethod - def Load(cls, fobj): - """Load data from the given stream. - - """ - return cls.FromDict(simplejson.load(fobj)) - - def Dumps(self): - """Dump and return the string representation.""" - buf = StringIO() - self.Dump(buf) - return buf.getvalue() - - @classmethod - def Loads(cls, data): - """Load data from a string.""" - return cls.Load(StringIO(data)) - def ToDict(self): """Convert to a dict holding only standard python types. @@ -189,15 +164,21 @@ class TaggableObject(ConfigObject): __slots__ = ConfigObject.__slots__ + ["tags"] @staticmethod - def ValidateTag(tag): + def ValidateTag(tag, removal=False): """Check if a tag is valid. If the tag is invalid, an errors.TagError will be raised. The function has no return value. + Args: + tag: Tag value + removal: Validating tag for removal? + """ if not isinstance(tag, basestring): raise errors.TagError("Invalid tag type (not a string)") + if removal: + return if len(tag) > constants.MAX_TAG_LEN: raise errors.TagError("Tag too long (>%d characters)" % constants.MAX_TAG_LEN) @@ -229,7 +210,7 @@ class TaggableObject(ConfigObject): """Remove a tag. """ - self.ValidateTag(tag) + self.ValidateTag(tag, removal=True) tags = self.GetTags() try: tags.remove(tag) @@ -302,15 +283,45 @@ class Disk(ConfigObject): def CreateOnSecondary(self): """Test if this device needs to be created on a secondary node.""" - return self.dev_type in ("drbd", "lvm") + return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8, + constants.LD_LV) def AssembleOnSecondary(self): """Test if this device needs to be assembled on a secondary node.""" - return self.dev_type in ("drbd", "lvm") + return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8, + constants.LD_LV) def OpenOnSecondary(self): """Test if this device needs to be opened on a secondary node.""" - return self.dev_type in ("lvm",) + return self.dev_type in (constants.LD_LV,) + + def StaticDevPath(self): + """Return the device path if this device type has a static one. + + Some devices (LVM for example) live always at the same /dev/ path, + irrespective of their status. For such devices, we return this + path, for others we return None. + + """ + if self.dev_type == constants.LD_LV: + return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1]) + return None + + def ChildrenNeeded(self): + """Compute the needed number of children for activation. + + This method will return either -1 (all children) or a positive + number denoting the minimum number of children needed for + activation (only mirrored devices will usually return >=0). + + Currently, only DRBD8 supports diskless activation (therefore we + return 0), for all other we keep the previous semantics and return + -1. + + """ + if self.dev_type == constants.LD_DRBD8: + return 0 + return -1 def GetNodes(self, node): """This function returns the nodes this device lives on. @@ -321,9 +332,9 @@ class Disk(ConfigObject): devices needs to (or can) be assembled. """ - if self.dev_type == "lvm" or self.dev_type == "md_raid1": + if self.dev_type == constants.LD_LV or self.dev_type == constants.LD_MD_R1: result = [node] - elif self.dev_type == "drbd": + elif self.dev_type in constants.LDS_DRBD: result = [self.logical_id[0], self.logical_id[1]] if node not in result: raise errors.ConfigurationError("DRBD device passed unknown node") @@ -369,6 +380,68 @@ class Disk(ConfigObject): # be different) return result + def RecordGrow(self, amount): + """Update the size of this disk after growth. + + This method recurses over the disks's children and updates their + size correspondigly. The method needs to be kept in sync with the + actual algorithms from bdev. + + """ + if self.dev_type == constants.LD_LV: + self.size += amount + elif self.dev_type == constants.LD_DRBD8: + if self.children: + self.children[0].RecordGrow(amount) + self.size += amount + else: + raise errors.ProgrammerError("Disk.RecordGrow called for unsupported" + " disk type %s" % self.dev_type) + + def SetPhysicalID(self, target_node, nodes_ip): + """Convert the logical ID to the physical ID. + + This is used only for drbd, which needs ip/port configuration. + + The routine descends down and updates its children also, because + this helps when the only the top device is passed to the remote + node. + + Arguments: + - target_node: the node we wish to configure for + - nodes_ip: a mapping of node name to ip + + The target_node must exist in in nodes_ip, and must be one of the + nodes in the logical ID for each of the DRBD devices encountered + in the disk tree. + + """ + if self.children: + for child in self.children: + child.SetPhysicalID(target_node, nodes_ip) + + if self.logical_id is None and self.physical_id is not None: + return + if self.dev_type in constants.LDS_DRBD: + pnode, snode, port = self.logical_id + if target_node not in (pnode, snode): + raise errors.ConfigurationError("DRBD device not knowing node %s" % + target_node) + pnode_ip = nodes_ip.get(pnode, None) + snode_ip = nodes_ip.get(snode, None) + if pnode_ip is None or snode_ip is None: + raise errors.ConfigurationError("Can't find primary or secondary node" + " for %s" % str(self)) + if pnode == target_node: + self.physical_id = (pnode_ip, port, + snode_ip, port) + else: # it must be secondary, we tested above + self.physical_id = (snode_ip, port, + pnode_ip, port) + else: + self.physical_id = self.logical_id + return + def ToDict(self): """Disk-specific conversion to standard python types. @@ -398,6 +471,43 @@ class Disk(ConfigObject): obj.physical_id = tuple(obj.physical_id) return obj + def __str__(self): + """Custom str() formatter for disks. + + """ + if self.dev_type == constants.LD_LV: + val = "