diff options
author | George Kraft <george.kraft@calxeda.com> | 2013-07-18 17:02:56 -0500 |
---|---|---|
committer | George Kraft <george.kraft@calxeda.com> | 2013-07-22 14:14:44 -0500 |
commit | 9ad87dac6b097953be20d95369f0c26a8844b4bb (patch) | |
tree | e80deaa5d295f8e3f3f37f6793567a309704720d | |
parent | b702cc5a1e9ebf8142d6301f3d5c7457f07ed9cb (diff) | |
download | cxmanage-9ad87dac6b097953be20d95369f0c26a8844b4bb.tar.gz |
Fabric: Add a CompositeBMC class for parallelizing BMC method calls
This provides a mechanism so we can easily parallelize BMC calls
without having to manually write wrapper functions in the Node and
Fabric classes.
>>> fabric.cbmc.sel_clear()
-rw-r--r-- | cxmanage_api/fabric.py | 48 | ||||
-rw-r--r-- | cxmanage_api/tasks.py | 9 | ||||
-rw-r--r-- | cxmanage_test/fabric_test.py | 19 |
3 files changed, 72 insertions, 4 deletions
diff --git a/cxmanage_api/fabric.py b/cxmanage_api/fabric.py index d0d7a10..bd8d6a7 100644 --- a/cxmanage_api/fabric.py +++ b/cxmanage_api/fabric.py @@ -56,6 +56,53 @@ class Fabric(object): :type node: `Node <node.html>`_ """ + class CompositeBMC(object): + """ Composite BMC object. Provides a mechanism to run BMC + commands in parallel across all nodes. + """ + + def __init__(self, fabric): + self.fabric = fabric + + def __getattr__(self, name): + """ If the underlying BMCs have a method by this name, then return + a callable function that does it in parallel across all nodes. + """ + nodes = self.fabric.nodes + task_queue = self.fabric.task_queue + + for node in nodes.values(): + if ((not hasattr(node.bmc, name)) or + (not hasattr(getattr(node.bmc, name), "__call__"))): + raise AttributeError( + "'CompositeBMC' object has no attribute '%s'" + % name + ) + + def function(*args, **kwargs): + """ Run the named BMC command in parallel across all nodes. """ + tasks = {} + for node_id, node in nodes.iteritems(): + tasks[node_id] = task_queue.put( + getattr(node.bmc, name), + *args, + **kwargs + ) + + results = {} + errors = {} + for node_id, task in tasks.items(): + task.join() + if task.status == "Completed": + results[node_id] = task.result + else: + errors[node_id] = task.error + if errors: + raise CommandFailedError(results, errors) + return results + + return function + def __init__(self, ip_address, username="admin", password="admin", tftp=None, ecme_tftp_port=5001, task_queue=None, verbose=False, node=None): @@ -68,6 +115,7 @@ class Fabric(object): self.task_queue = task_queue self.verbose = verbose self.node = node + self.cbmc = Fabric.CompositeBMC(self) self._nodes = {} diff --git a/cxmanage_api/tasks.py b/cxmanage_api/tasks.py index 6b5cfde..7ed7851 100644 --- a/cxmanage_api/tasks.py +++ b/cxmanage_api/tasks.py @@ -43,7 +43,7 @@ class Task(object): :type args: list """ - def __init__(self, method, *args): + def __init__(self, method, *args, **kwargs): """Default constructor for the Task class.""" self.status = "Queued" self.result = None @@ -51,6 +51,7 @@ class Task(object): self._method = method self._args = args + self._kwargs = kwargs self._finished = Event() def join(self): @@ -70,7 +71,7 @@ class Task(object): """Execute this task. Should only be called by TaskWorker.""" self.status = "In Progress" try: - self.result = self._method(*self._args) + self.result = self._method(*self._args, **self._kwargs) self.status = "Completed" except Exception as e: self.error = e @@ -96,7 +97,7 @@ class TaskQueue(object): self._queue = deque() self._workers = 0 - def put(self, method, *args): + def put(self, method, *args, **kwargs): """Add a task to the task queue, and spawn a worker if we're not full. :param method: Named method to run. @@ -110,7 +111,7 @@ class TaskQueue(object): """ self._lock.acquire() - task = Task(method, *args) + task = Task(method, *args, **kwargs) self._queue.append(task) if self._workers < self.threads: diff --git a/cxmanage_test/fabric_test.py b/cxmanage_test/fabric_test.py index 9d6de38..f2720c9 100644 --- a/cxmanage_test/fabric_test.py +++ b/cxmanage_test/fabric_test.py @@ -407,6 +407,25 @@ class FabricTest(unittest.TestCase): else: self.assertEqual(node.bmc.executed, []) + def test_composite_bmc(self): + """ Test the CompositeBMC member """ + with self.assertRaises(AttributeError): + self.fabric.cbmc.fake_method + + self.fabric.cbmc.set_chassis_power("off") + results = self.fabric.cbmc.get_chassis_status() + + self.assertEqual(len(results), len(self.fabric.nodes)) + for node_id in self.fabric.nodes: + self.assertFalse(results[node_id].power_on) + + for node in self.fabric.nodes.values(): + self.assertEqual(node.bmc.executed, [ + ("set_chassis_power", "off"), + "get_chassis_status" + ]) + + class DummyNode(object): """ Dummy node for the nodemanager tests """ def __init__(self, ip_address, username="admin", password="admin", |