summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com>2013-07-22 17:16:40 -0500
committerSheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com>2013-07-22 17:16:40 -0500
commit26bf2b8a0248815e0430042617c73b2f1b7c9d4f (patch)
tree9c7ca611cd0d19c72a10044b9814cbb357350ebb
parentbefd8737f2c89e6ab74f79796f9520c2a82a90f0 (diff)
parentf09a00207940c1413a7e3f5fe728479cf8f1f079 (diff)
downloadcxmanage-26bf2b8a0248815e0430042617c73b2f1b7c9d4f.tar.gz
Trivial merge
Merge branch 'master' of ssh://git.calxeda.com/var/git/cx_manage_util
-rw-r--r--cxmanage_api/cx_exceptions.py19
-rw-r--r--cxmanage_api/fabric.py96
-rw-r--r--cxmanage_api/node.py7
-rw-r--r--cxmanage_api/tasks.py9
-rw-r--r--cxmanage_api/ubootenv.py16
-rw-r--r--cxmanage_test/fabric_test.py67
-rw-r--r--cxmanage_test/node_test.py14
-rwxr-xr-xscripts/cxmanage4
-rw-r--r--setup.py4
9 files changed, 203 insertions, 33 deletions
diff --git a/cxmanage_api/cx_exceptions.py b/cxmanage_api/cx_exceptions.py
index b74a927..f390392 100644
--- a/cxmanage_api/cx_exceptions.py
+++ b/cxmanage_api/cx_exceptions.py
@@ -259,26 +259,25 @@ class InvalidImageError(Exception):
return self.msg
-class UnknownBootCmdError(Exception):
- """Raised when the boot command is not: run bootcmd_pxe, run bootcmd_sata,
- run bootcmd_mmc, setenv bootdevice, or reset.
+class UbootenvError(Exception):
+ """Raised when the UbootEnv class fails to interpret the ubootenv
+ environment variables.
- >>> from cxmanage_api.cx_exceptions import UnknownBootCmdError
- >>> raise UnknownBootCmdError('My custom exception text!')
+ >>> from cxmanage_api.cx_exceptions import UbootenvError
+ >>> raise UbootenvError('My custom exception text!')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
- cxmanage_api.cx_exceptions.UnknownBootCmdError: My custom exception text!
+ cxmanage_api.cx_exceptions.UbootenvError: My custom exception text!
:param msg: Exceptions message and details to return to the user.
:type msg: string
- :raised: When the boot command is not: run bootcmd_pxe, run bootcmd_sata,
- run bootcmd_mmc, setenv bootdevice, or reset.
+ :raised: When ubootenv settings are unrecognizable.
"""
def __init__(self, msg):
- """Default constructor for the UnknownBootCmdError class."""
- super(UnknownBootCmdError, self).__init__()
+ """Default constructor for the UbootenvError class."""
+ super(UbootenvError, self).__init__()
self.msg = msg
def __str__(self):
diff --git a/cxmanage_api/fabric.py b/cxmanage_api/fabric.py
index 2e140f5..7fc0192 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 = {}
@@ -183,10 +231,6 @@ class Fabric(object):
3: ['fc:2f:40:88:b3:6c', 'fc:2f:40:88:b3:6d', 'fc:2f:40:88:b3:6e']
}
- :param async: Flag that determines if the command result (dictionary)
- is returned or a Task object (can get status, etc.).
- :type async: boolean
-
:return: The MAC addresses for each node.
:rtype: dictionary
@@ -792,6 +836,50 @@ class Fabric(object):
self.primary_node.bmc.fabric_rm_macaddr(nodeid=nodeid, iface=iface,
macaddr=macaddr)
+ def set_macaddr_base(self, macaddr):
+ """ Set a base MAC address for a custom range.
+
+ >>> fabric.set_macaddr_base("66:55:44:33:22:11")
+
+ :param macaddr: mac address base to use
+ :type macaddr: string
+
+ """
+ self.primary_node.bmc.fabric_config_set_macaddr_base(macaddr=macaddr)
+
+ def get_macaddr_base(self):
+ """ Get the base MAC address for custom ranges.
+
+ >>> fabric.get_macaddr_base()
+ '08:00:00:00:08:5c'
+
+ :return: mac address base
+ :rtype: string
+ """
+ return self.primary_node.bmc.fabric_config_get_macaddr_base()
+
+ def set_macaddr_mask(self, mask):
+ """ Set MAC address mask for a custom range.
+
+ >>> fabric.set_macaddr_mask("ff:ff:ff:ff:ff:00")
+
+ :param macaddr: mac address mask to use
+ :type macaddr: string
+
+ """
+ self.primary_node.bmc.fabric_config_set_macaddr_mask(mask=mask)
+
+ def get_macaddr_mask(self):
+ """ Get the MAC address mask for custom ranges.
+
+ >>> fabric.get_macaddr_mask()
+ '08:00:00:00:08:5c'
+
+ :return: mac address mask
+ :rtype: string
+ """
+ return self.primary_node.bmc.fabric_config_get_macaddr_mask()
+
def get_linkspeed_policy(self):
"""Get the global linkspeed policy for the fabric. In the partition
world this means the linkspeed for Configuration 0, Partition 0,
diff --git a/cxmanage_api/node.py b/cxmanage_api/node.py
index 9edd672..b499f36 100644
--- a/cxmanage_api/node.py
+++ b/cxmanage_api/node.py
@@ -49,7 +49,8 @@ from cxmanage_api.ubootenv import UbootEnv as UBOOTENV
from cxmanage_api.ip_retriever import IPRetriever as IPRETRIEVER
from cxmanage_api.cx_exceptions import TimeoutError, NoSensorError, \
SocmanVersionError, FirmwareConfigError, PriorityIncrementError, \
- NoPartitionError, TransferFailure, ImageSizeError, PartitionInUseError
+ NoPartitionError, TransferFailure, ImageSizeError, \
+ PartitionInUseError, UbootenvError
class Node(object):
@@ -723,7 +724,7 @@ class Node(object):
ubootenv.set_pxe_interface(old_ubootenv.get_pxe_interface())
logger.info(
- "Set boot order to " + old_ubootenv.get_boot_order()
+ "Set boot order to %s" % old_ubootenv.get_boot_order()
)
filename = temp_file()
@@ -739,7 +740,7 @@ class Node(object):
"Done uploading ubootenv image to first " + \
"partition ('running partition')"
)
- except (ValueError, Exception):
+ except (ValueError, UbootenvError):
self._upload_image(image, running_part, priority)
updated_partitions += [running_part, factory_part]
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_api/ubootenv.py b/cxmanage_api/ubootenv.py
index 12d550d..cd1a35a 100644
--- a/cxmanage_api/ubootenv.py
+++ b/cxmanage_api/ubootenv.py
@@ -33,7 +33,7 @@ import struct
from cxmanage_api.simg import has_simg, get_simg_contents
from cxmanage_api.crc32 import get_crc32
-from cxmanage_api.cx_exceptions import UnknownBootCmdError
+from cxmanage_api.cx_exceptions import UbootenvError
ENVIRONMENT_SIZE = 8192
@@ -87,7 +87,7 @@ class UbootEnv:
:raises ValueError: If an invalid boot device is specified.
:raises ValueError: If 'retry' and 'reset' args are used together.
- :raises Exception: If the u-boot environment is unrecognized
+ :raises UbootenvError: If the u-boot environment is unrecognized
"""
validate_boot_args(boot_args)
@@ -103,7 +103,7 @@ class UbootEnv:
elif all(x in self.variables for x in UBOOTENV_V2_VARIABLES):
version = 2
else:
- raise Exception("Unrecognized u-boot environment")
+ raise UbootenvError("Unrecognized u-boot environment")
for arg in boot_args:
if arg == "retry":
@@ -159,7 +159,7 @@ class UbootEnv:
:returns: Boot order for this U-Boot Environment.
:rtype: string
- :raises UnknownBootCmdError: If a boot command is unrecognized.
+ :raises UbootenvError: If a boot command is unrecognized.
"""
boot_args = []
@@ -171,7 +171,7 @@ class UbootEnv:
elif target == "scsi":
boot_args.append("disk")
else:
- raise UnknownBootCmdError("Unrecognized boot target: %s"
+ raise UbootenvError("Unrecognized boot target: %s"
% target)
else:
if "bootcmd_default" in self.variables:
@@ -198,7 +198,7 @@ class UbootEnv:
boot_args.append("reset")
break
else:
- raise UnknownBootCmdError("Unrecognized boot command: %s"
+ raise UbootenvError("Unrecognized boot command: %s"
% command)
if retry:
@@ -249,7 +249,7 @@ class UbootEnv:
:returns: Boot order for this U-Boot Environment.
:rtype: string
- :raises Exception: If the u-boot environment value is not recognized.
+ :raises ValueError: If the u-boot environment value is not recognized.
"""
@@ -264,7 +264,7 @@ class UbootEnv:
elif (xgmac == "xgmac1"):
return "eth1"
else:
- raise Exception("Unrecognized value for ethprime")
+ raise ValueError("Unrecognized value for ethprime")
else:
return "eth0"
diff --git a/cxmanage_test/fabric_test.py b/cxmanage_test/fabric_test.py
index 0dd2593..f2720c9 100644
--- a/cxmanage_test/fabric_test.py
+++ b/cxmanage_test/fabric_test.py
@@ -359,6 +359,73 @@ class FabricTest(unittest.TestCase):
bmc = self.fabric.primary_node.bmc
self.assertIn ('fabric_rm_macaddr', bmc.executed)
+ def test_set_macaddr_base(self):
+ """Test the set_macaddr_base method"""
+ self.fabric.set_macaddr_base("00:11:22:33:44:55")
+ for node in self.fabric.nodes.values():
+ if node == self.fabric.primary_node:
+ self.assertEqual(
+ node.bmc.executed,
+ [("fabric_config_set_macaddr_base", "00:11:22:33:44:55")]
+ )
+ else:
+ self.assertEqual(node.bmc.executed, [])
+
+ def test_get_macaddr_base(self):
+ """Test the get_macaddr_base method"""
+ self.assertEqual(self.fabric.get_macaddr_base(), "00:00:00:00:00:00")
+ for node in self.fabric.nodes.values():
+ if node == self.fabric.primary_node:
+ self.assertEqual(
+ node.bmc.executed,
+ ["fabric_config_get_macaddr_base"]
+ )
+ else:
+ self.assertEqual(node.bmc.executed, [])
+
+ def test_set_macaddr_mask(self):
+ """Test the set_macaddr_mask method"""
+ self.fabric.set_macaddr_mask("00:11:22:33:44:55")
+ for node in self.fabric.nodes.values():
+ if node == self.fabric.primary_node:
+ self.assertEqual(
+ node.bmc.executed,
+ [("fabric_config_set_macaddr_mask", "00:11:22:33:44:55")]
+ )
+ else:
+ self.assertEqual(node.bmc.executed, [])
+
+ def test_get_macaddr_mask(self):
+ """Test the get_macaddr_mask method"""
+ self.assertEqual(self.fabric.get_macaddr_mask(), "00:00:00:00:00:00")
+ for node in self.fabric.nodes.values():
+ if node == self.fabric.primary_node:
+ self.assertEqual(
+ node.bmc.executed,
+ ["fabric_config_get_macaddr_mask"]
+ )
+ 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",
diff --git a/cxmanage_test/node_test.py b/cxmanage_test/node_test.py
index 4f843a7..b6f860b 100644
--- a/cxmanage_test/node_test.py
+++ b/cxmanage_test/node_test.py
@@ -847,6 +847,20 @@ class DummyBMC(LanBMC):
self.fabric_lu_factor = lu_factor
self.executed.append('fabric_config_set_link_users_factor')
+ def fabric_config_set_macaddr_base(self, macaddr):
+ self.executed.append(('fabric_config_set_macaddr_base', macaddr))
+
+ def fabric_config_get_macaddr_base(self):
+ self.executed.append('fabric_config_get_macaddr_base')
+ return "00:00:00:00:00:00"
+
+ def fabric_config_set_macaddr_mask(self, mask):
+ self.executed.append(('fabric_config_set_macaddr_mask', mask))
+
+ def fabric_config_get_macaddr_mask(self):
+ self.executed.append('fabric_config_get_macaddr_mask')
+ return "00:00:00:00:00:00"
+
def fabric_add_macaddr(self, nodeid=0, iface=0, macaddr=None):
self.executed.append('fabric_add_macaddr')
diff --git a/scripts/cxmanage b/scripts/cxmanage
index 889fe4e..ccde835 100755
--- a/scripts/cxmanage
+++ b/scripts/cxmanage
@@ -50,8 +50,8 @@ from cxmanage.commands.ipdiscover import ipdiscover_command
from cxmanage.commands.tspackage import tspackage_command
-PYIPMI_VERSION = '0.7.1'
-IPMITOOL_VERSION = '1.8.11.0-cx5'
+PYIPMI_VERSION = '0.8.0'
+IPMITOOL_VERSION = '1.8.11.0-cx7'
PARSER_EPILOG = """examples:
diff --git a/setup.py b/setup.py
index bd49b13..4e2f47d 100644
--- a/setup.py
+++ b/setup.py
@@ -33,7 +33,7 @@ from setuptools import setup
setup(
name='cxmanage',
- version='0.8.2',
+ version='0.9.0',
packages=['cxmanage', 'cxmanage.commands', 'cxmanage_api'],
scripts=['scripts/cxmanage', 'scripts/sol_tabs'],
description='Calxeda Management Utility',
@@ -42,7 +42,7 @@ setup(
install_requires=[
'tftpy',
'pexpect',
- 'pyipmi>=0.7.1',
+ 'pyipmi>=0.8.0',
'argparse',
],
extras_require={