Merge "Implement version 2 payload with more hw info"
This commit is contained in:
commit
7d4621eb13
@ -18,6 +18,7 @@ import abc
|
||||
import functools
|
||||
import os
|
||||
|
||||
import psutil
|
||||
import six
|
||||
import stevedore
|
||||
|
||||
@ -45,26 +46,19 @@ class HardwareType(object):
|
||||
MAC_ADDRESS = 'mac_address'
|
||||
|
||||
|
||||
class HardwareInfo(encoding.Serializable):
|
||||
def __init__(self, type, id):
|
||||
self.type = type
|
||||
self.id = id
|
||||
class BlockDevice(encoding.Serializable):
|
||||
def __init__(self, name, size):
|
||||
self.name = name
|
||||
self.size = size
|
||||
|
||||
def serialize(self):
|
||||
return utils.get_ordereddict([
|
||||
('type', self.type),
|
||||
('id', self.id),
|
||||
('name', self.name),
|
||||
('size', self.size),
|
||||
])
|
||||
|
||||
|
||||
class BlockDevice(object):
|
||||
def __init__(self, name, size, start_sector):
|
||||
self.name = name
|
||||
self.size = size
|
||||
self.start_sector = start_sector
|
||||
|
||||
|
||||
class NetworkInterface(object):
|
||||
class NetworkInterface(encoding.Serializable):
|
||||
def __init__(self, name, mac_addr):
|
||||
self.name = name
|
||||
self.mac_address = mac_addr
|
||||
@ -72,6 +66,38 @@ class NetworkInterface(object):
|
||||
self.switch_port_descr = None
|
||||
self.switch_chassis_descr = None
|
||||
|
||||
def serialize(self):
|
||||
return utils.get_ordereddict([
|
||||
('name', self.name),
|
||||
('mac_address', self.mac_address),
|
||||
('switch_port_descr', self.switch_port_descr),
|
||||
('switch_chassis_descr', self.switch_port_descr),
|
||||
])
|
||||
|
||||
|
||||
class CPU(encoding.Serializable):
|
||||
def __init__(self, model_name, frequency, count):
|
||||
self.model_name = model_name
|
||||
self.frequency = frequency
|
||||
self.count = count
|
||||
|
||||
def serialize(self):
|
||||
return utils.get_ordereddict([
|
||||
('model_name', self.model_name),
|
||||
('frequency', self.frequency),
|
||||
('count', self.count),
|
||||
])
|
||||
|
||||
|
||||
class Memory(encoding.Serializable):
|
||||
def __init__(self, total):
|
||||
self.total = total
|
||||
|
||||
def serialize(self):
|
||||
return utils.get_ordereddict([
|
||||
('total', self.total),
|
||||
])
|
||||
|
||||
|
||||
class HardwareManager(object):
|
||||
@abc.abstractmethod
|
||||
@ -87,10 +113,11 @@ class HardwareManager(object):
|
||||
pass
|
||||
|
||||
def list_hardware_info(self):
|
||||
hardware_info = []
|
||||
for interface in self.list_network_interfaces():
|
||||
hardware_info.append(HardwareInfo(HardwareType.MAC_ADDRESS,
|
||||
interface.mac_address))
|
||||
hardware_info = {}
|
||||
hardware_info['interfaces'] = self.list_network_interfaces()
|
||||
hardware_info['cpu'] = self.get_cpus()
|
||||
hardware_info['disks'] = self.list_block_devices()
|
||||
hardware_info['memory'] = self.get_memory()
|
||||
return hardware_info
|
||||
|
||||
|
||||
@ -123,7 +150,26 @@ class GenericHardwareManager(HardwareManager):
|
||||
for name in iface_names
|
||||
if self._is_device(name)]
|
||||
|
||||
def _list_block_devices(self):
|
||||
def get_cpus(self):
|
||||
model = None
|
||||
freq = None
|
||||
with open('/proc/cpuinfo') as f:
|
||||
lines = f.read()
|
||||
for line in lines.split('\n'):
|
||||
if model and freq:
|
||||
break
|
||||
if not model and line.startswith('model name'):
|
||||
model = line.split(':')[1].strip()
|
||||
if not freq and line.startswith('cpu MHz'):
|
||||
freq = line.split(':')[1].strip()
|
||||
|
||||
return CPU(model, freq, psutil.cpu_count())
|
||||
|
||||
def get_memory(self):
|
||||
# psutil returns a long, force it to an int
|
||||
return Memory(int(psutil.phymem_usage().total))
|
||||
|
||||
def list_block_devices(self):
|
||||
report = utils.execute('blockdev', '--report',
|
||||
check_exit_code=[0])[0]
|
||||
lines = report.split('\n')
|
||||
@ -131,20 +177,17 @@ class GenericHardwareManager(HardwareManager):
|
||||
startsec_idx = lines[0].index('StartSec')
|
||||
device_idx = lines[0].index('Device')
|
||||
size_idx = lines[0].index('Size')
|
||||
# If a device doesn't start at sector 0, assume it is a partition
|
||||
return [BlockDevice(line[device_idx],
|
||||
int(line[size_idx]),
|
||||
int(line[startsec_idx]))
|
||||
int(line[size_idx]))
|
||||
for line
|
||||
in lines[1:]]
|
||||
in lines[1:] if int(line[startsec_idx]) == 0]
|
||||
|
||||
def get_os_install_device(self):
|
||||
# Assume anything with a start sector other than 0, is a partition
|
||||
block_devices = [device for device in self._list_block_devices()
|
||||
if device.start_sector == 0]
|
||||
|
||||
# Find the first device larger than 4GB, assume it is the OS disk
|
||||
# TODO(russellhaering): This isn't a valid assumption in all cases,
|
||||
# is there a more reasonable default behavior?
|
||||
block_devices = self.list_block_devices()
|
||||
block_devices.sort(key=lambda device: device.size)
|
||||
for device in block_devices:
|
||||
if device.size >= (4 * pow(1024, 3)):
|
||||
|
@ -30,7 +30,7 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
class APIClient(object):
|
||||
api_version = 'v1'
|
||||
payload_version = '1'
|
||||
payload_version = '2'
|
||||
|
||||
def __init__(self, api_url):
|
||||
self.api_url = api_url.rstrip('/')
|
||||
|
@ -65,16 +65,100 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
|
||||
'--report',
|
||||
check_exit_code=[0])
|
||||
|
||||
def test_list_hardwre_info(self):
|
||||
@mock.patch('psutil.cpu_count')
|
||||
@mock.patch(OPEN_FUNCTION_NAME)
|
||||
def test_get_cpus(self, mocked_open, mocked_cpucount):
|
||||
mocked_open.return_value.__enter__ = lambda s: s
|
||||
mocked_open.return_value.__exit__ = mock.Mock()
|
||||
read_mock = mocked_open.return_value.read
|
||||
read_mock.return_value = (
|
||||
'processor : 0\n'
|
||||
'vendor_id : GenuineIntel\n'
|
||||
'cpu family : 6\n'
|
||||
'model : 58\n'
|
||||
'model name : Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz\n'
|
||||
'stepping : 9\n'
|
||||
'microcode : 0x15\n'
|
||||
'cpu MHz : 2594.685\n'
|
||||
'cache size : 6144 KB\n'
|
||||
'fpu : yes\n'
|
||||
'fpu_exception : yes\n'
|
||||
'cpuid level : 13\n'
|
||||
'wp : yes\n'
|
||||
'flags : fpu vme de pse tsc msr pae mce cx8 apic sep '
|
||||
'mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss '
|
||||
'syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl '
|
||||
'xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni '
|
||||
'pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt aes xsave '
|
||||
'avx f16c rdrand hypervisor lahf_lm ida arat epb xsaveopt pln pts '
|
||||
'dtherm fsgsbase smep\n'
|
||||
'bogomips : 5189.37\n'
|
||||
'clflush size : 64\n'
|
||||
'cache_alignment : 64\n'
|
||||
'address sizes : 40 bits physical, 48 bits virtual\n'
|
||||
'power management:\n'
|
||||
'\n'
|
||||
'processor : 1\n'
|
||||
'vendor_id : GenuineIntel\n'
|
||||
'cpu family : 6\n'
|
||||
'model : 58\n'
|
||||
'model name : Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz\n'
|
||||
'stepping : 9\n'
|
||||
'microcode : 0x15\n'
|
||||
'cpu MHz : 2594.685\n'
|
||||
'cache size : 6144 KB\n'
|
||||
'fpu : yes\n'
|
||||
'fpu_exception : yes\n'
|
||||
'cpuid level : 13\n'
|
||||
'wp : yes\n'
|
||||
'flags : fpu vme de pse tsc msr pae mce cx8 apic sep '
|
||||
'mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss '
|
||||
'syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts nopl '
|
||||
'xtopology tsc_reliable nonstop_tsc aperfmperf eagerfpu pni '
|
||||
'pclmulqdq ssse3 cx16 pcid sse4_1 sse4_2 x2apic popcnt aes xsave '
|
||||
'avx f16c rdrand hypervisor lahf_lm ida arat epb xsaveopt pln pts '
|
||||
'dtherm fsgsbase smep\n'
|
||||
'bogomips : 5189.37\n'
|
||||
'clflush size : 64\n'
|
||||
'cache_alignment : 64\n'
|
||||
'address sizes : 40 bits physical, 48 bits virtual\n'
|
||||
'power management:\n'
|
||||
)
|
||||
|
||||
mocked_cpucount.return_value = 2
|
||||
|
||||
cpus = self.hardware.get_cpus()
|
||||
self.assertEqual(cpus.model_name,
|
||||
'Intel(R) Core(TM) i7-3720QM CPU @ 2.60GHz')
|
||||
self.assertEqual(cpus.frequency, '2594.685')
|
||||
self.assertEqual(cpus.count, 2)
|
||||
|
||||
def test_list_hardware_info(self):
|
||||
self.hardware.list_network_interfaces = mock.Mock()
|
||||
self.hardware.list_network_interfaces.return_value = [
|
||||
hardware.NetworkInterface('eth0', '00:0c:29:8c:11:b1'),
|
||||
hardware.NetworkInterface('eth1', '00:0c:29:8c:11:b2'),
|
||||
]
|
||||
|
||||
self.hardware.get_cpus = mock.Mock()
|
||||
self.hardware.get_cpus.return_value = hardware.CPU(
|
||||
'Awesome CPU x14 9001',
|
||||
9001,
|
||||
14)
|
||||
|
||||
self.hardware.get_memory = mock.Mock()
|
||||
self.hardware.get_memory.return_value = hardware.Memory(1017012)
|
||||
|
||||
self.hardware.list_block_devices = mock.Mock()
|
||||
self.hardware.list_block_devices.return_value = [
|
||||
hardware.BlockDevice('/dev/sdj', 1073741824),
|
||||
hardware.BlockDevice('/dev/hdaa', 65535),
|
||||
]
|
||||
|
||||
hardware_info = self.hardware.list_hardware_info()
|
||||
self.assertEqual(len(hardware_info), 2)
|
||||
self.assertEqual(hardware_info[0].type, 'mac_address')
|
||||
self.assertEqual(hardware_info[1].type, 'mac_address')
|
||||
self.assertEqual(hardware_info[0].id, '00:0c:29:8c:11:b1')
|
||||
self.assertEqual(hardware_info[1].id, '00:0c:29:8c:11:b2')
|
||||
self.assertEqual(hardware_info['memory'], self.hardware.get_memory())
|
||||
self.assertEqual(hardware_info['cpu'], self.hardware.get_cpus())
|
||||
self.assertEqual(hardware_info['disks'],
|
||||
self.hardware.list_block_devices())
|
||||
self.assertEqual(hardware_info['interfaces'],
|
||||
self.hardware.list_network_interfaces())
|
||||
|
@ -41,12 +41,18 @@ class TestBaseIronicPythonAgent(test_base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestBaseIronicPythonAgent, self).setUp()
|
||||
self.api_client = ironic_api_client.APIClient(API_URL)
|
||||
self.hardware_info = [
|
||||
hardware.HardwareInfo(hardware.HardwareType.MAC_ADDRESS,
|
||||
'aa:bb:cc:dd:ee:ff'),
|
||||
hardware.HardwareInfo(hardware.HardwareType.MAC_ADDRESS,
|
||||
'ff:ee:dd:cc:bb:aa'),
|
||||
]
|
||||
self.hardware_info = {
|
||||
'interfaces': [
|
||||
hardware.NetworkInterface('eth0', '00:0c:29:8c:11:b1'),
|
||||
hardware.NetworkInterface('eth1', '00:0c:29:8c:11:b2'),
|
||||
],
|
||||
'cpu': hardware.CPU('Awesome Jay CPU x10 9001', '9001', '10'),
|
||||
'disks': [
|
||||
hardware.BlockDevice('/dev/sdj', '9001'),
|
||||
hardware.BlockDevice('/dev/hdj', '9002'),
|
||||
],
|
||||
'memory': hardware.Memory('8675309'),
|
||||
}
|
||||
|
||||
def test_successful_heartbeat(self):
|
||||
expected_heartbeat_before = time.time() + 120
|
||||
@ -65,7 +71,7 @@ class TestBaseIronicPythonAgent(test_base.BaseTestCase):
|
||||
self.assertEqual(heartbeat_before, expected_heartbeat_before)
|
||||
|
||||
heartbeat_path = 'v1/nodes/deadbeef-dabb-ad00-b105-f00d00bab10c/' \
|
||||
'vendor_passthru/heartbeat'
|
||||
'vendor_passthru/heartbeat'
|
||||
request_args = self.api_client.session.request.call_args[0]
|
||||
self.assertEqual(request_args[0], 'POST')
|
||||
self.assertEqual(request_args[1], API_URL + heartbeat_path)
|
||||
@ -162,16 +168,40 @@ class TestBaseIronicPythonAgent(test_base.BaseTestCase):
|
||||
data = self.api_client.session.request.call_args[1]['data']
|
||||
content = json.loads(data)
|
||||
self.assertEqual(content['version'], self.api_client.payload_version)
|
||||
self.assertEqual(content['inventory'], [
|
||||
{
|
||||
'type': 'mac_address',
|
||||
'id': 'aa:bb:cc:dd:ee:ff',
|
||||
self.assertEqual(content['inventory'], {
|
||||
u'interfaces': [
|
||||
{
|
||||
u'mac_address': u'00:0c:29:8c:11:b1',
|
||||
u'name': u'eth0',
|
||||
u'switch_chassis_descr': None,
|
||||
u'switch_port_descr': None
|
||||
},
|
||||
{
|
||||
u'mac_address': u'00:0c:29:8c:11:b2',
|
||||
u'name': u'eth1',
|
||||
u'switch_chassis_descr': None,
|
||||
'switch_port_descr': None
|
||||
}
|
||||
],
|
||||
u'cpu': {
|
||||
u'model_name': u'Awesome Jay CPU x10 9001',
|
||||
u'frequency': u'9001',
|
||||
u'count': u'10',
|
||||
},
|
||||
{
|
||||
'type': 'mac_address',
|
||||
'id': 'ff:ee:dd:cc:bb:aa',
|
||||
u'disks': [
|
||||
{
|
||||
u'name': u'/dev/sdj',
|
||||
u'size': u'9001',
|
||||
},
|
||||
{
|
||||
u'name': u'/dev/hdj',
|
||||
u'size': u'9002',
|
||||
},
|
||||
],
|
||||
u'memory': {
|
||||
u'total': u'8675309',
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
def test_do_lookup_bad_response_code(self):
|
||||
response = FakeResponse(status_code=400, content={
|
||||
|
@ -9,3 +9,4 @@ oslo.config>=1.2.0
|
||||
Babel>=1.3
|
||||
iso8601>=0.1.9
|
||||
oslotest==1.0
|
||||
psutil>=1.1.1
|
||||
|
Loading…
x
Reference in New Issue
Block a user