Commit 6a654276 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

QA: Convert instances to objects



Up until now instances were stored as a dictionary. The keys were
hardcoded in a lot of places and entries modified directly.

This patch introduces a new class for instances in QA named
“_QaInstance”. It still supports accessing details via dictionary
syntax, but that will be removed after a couple of patches changing all
users. Unit tests for “qa_config.AcquireInstance” are also included.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarBernardo Dal Seno <bdalseno@google.com>
parent a77e3d33
......@@ -40,6 +40,84 @@ _ENABLED_HV_KEY = "enabled-hypervisors"
_config = None
class _QaInstance(object):
__slots__ = [
"name",
"nicmac",
"used",
"disk_template",
]
def __init__(self, name, nicmac):
"""Initializes instances of this class.
"""
self.name = name
self.nicmac = nicmac
self.used = None
self.disk_template = None
@classmethod
def FromDict(cls, data):
"""Creates instance object from JSON dictionary.
"""
nicmac = []
macaddr = data.get("nic.mac/0")
if macaddr:
nicmac.append(macaddr)
return cls(name=data["name"], nicmac=nicmac)
def __getitem__(self, key):
"""Legacy dict-like interface.
"""
if key == "name":
return self.name
else:
raise KeyError(key)
def get(self, key, default):
"""Legacy dict-like interface.
"""
try:
return self[key]
except KeyError:
return default
def GetNicMacAddr(self, idx, default):
"""Returns MAC address for NIC.
@type idx: int
@param idx: NIC index
@param default: Default value
"""
if len(self.nicmac) > idx:
return self.nicmac[idx]
else:
return default
_RESOURCE_CONVERTER = {
"instances": _QaInstance.FromDict,
}
def _ConvertResources((key, value)):
"""Converts cluster resources in configuration to Python objects.
"""
fn = _RESOURCE_CONVERTER.get(key, None)
if fn:
return (key, map(fn, value))
else:
return (key, value)
class _QaConfig(object):
def __init__(self, data):
"""Initializes instances of this class.
......@@ -61,7 +139,8 @@ class _QaConfig(object):
"""
data = serializer.LoadJson(utils.ReadFile(filename))
result = cls(data)
result = cls(dict(map(_ConvertResources,
data.items()))) # pylint: disable=E1103
result.Validate()
return result
......@@ -308,7 +387,7 @@ def GetInstanceNicMac(inst, default=None):
"""Returns MAC address for instance's network interface.
"""
return inst.get("nic.mac/0", default)
return inst.GetNicMacAddr(0, default)
def GetMasterNode():
......@@ -318,33 +397,41 @@ def GetMasterNode():
return GetConfig().GetMasterNode()
def AcquireInstance():
def AcquireInstance(_cfg=None):
"""Returns an instance which isn't in use.
"""
if _cfg is None:
cfg = GetConfig()
else:
cfg = _cfg
# Filter out unwanted instances
tmp_flt = lambda inst: not inst.get("_used", False)
instances = filter(tmp_flt, GetConfig()["instances"])
del tmp_flt
instances = filter(lambda inst: not inst.used, cfg["instances"])
if len(instances) == 0:
if not instances:
raise qa_error.OutOfInstancesError("No instances left")
inst = instances[0]
inst["_used"] = True
inst["_template"] = None
assert not inst.used
assert inst.disk_template is None
inst.used = True
return inst
def ReleaseInstance(inst):
inst["_used"] = False
inst.used = False
inst.disk_template = None
def GetInstanceTemplate(inst):
"""Return the disk template of an instance.
"""
templ = inst["_template"]
templ = inst.disk_template
assert templ is not None
return templ
......@@ -353,7 +440,7 @@ def SetInstanceTemplate(inst, template):
"""Set the disk template for an instance.
"""
inst["_template"] = template
inst.disk_template = template
def SetExclusiveStorage(value):
......
......@@ -25,6 +25,7 @@ import unittest
import tempfile
import shutil
import os
import operator
from ganeti import utils
from ganeti import serializer
......@@ -272,6 +273,37 @@ class TestQaConfig(unittest.TestCase):
else:
self.assertTrue(self.config.IsTemplateSupported(template))
def testInstanceConversion(self):
self.assertTrue(isinstance(self.config["instances"][0],
qa_config._QaInstance))
def testAcquireAndReleaseInstance(self):
self.assertFalse(compat.any(map(operator.attrgetter("used"),
self.config["instances"])))
inst = qa_config.AcquireInstance(_cfg=self.config)
self.assertTrue(inst.used)
self.assertTrue(inst.disk_template is None)
qa_config.ReleaseInstance(inst)
self.assertFalse(inst.used)
self.assertTrue(inst.disk_template is None)
self.assertFalse(compat.any(map(operator.attrgetter("used"),
self.config["instances"])))
def testAcquireInstanceTooMany(self):
# Acquire all instances
for _ in range(len(self.config["instances"])):
inst = qa_config.AcquireInstance(_cfg=self.config)
self.assertTrue(inst.used)
self.assertTrue(inst.disk_template is None)
# The next acquisition must fail
self.assertRaises(qa_error.OutOfInstancesError,
qa_config.AcquireInstance, _cfg=self.config)
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment