summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Kraft <george.kraft@calxeda.com>2013-07-18 17:02:56 -0500
committerGeorge Kraft <george.kraft@calxeda.com>2013-07-22 14:14:44 -0500
commit9ad87dac6b097953be20d95369f0c26a8844b4bb (patch)
treee80deaa5d295f8e3f3f37f6793567a309704720d
parentb702cc5a1e9ebf8142d6301f3d5c7457f07ed9cb (diff)
downloadcxmanage-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.py48
-rw-r--r--cxmanage_api/tasks.py9
-rw-r--r--cxmanage_test/fabric_test.py19
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",