Add "ProLiant BL460c Gen10 Graphics Exp" support and GPU expansion bay (#165)
* Add "ProLiant BL460c Gen10 Graphics Exp" * Add GPU expansion support for HP_ProLiant_BL460c_Gen10_Graphics_Exp * Add ProLiant BL460c Graphics Expansion Blade support in README * Dont crash if vendor other than HP * Typo
This commit is contained in:
parent
0fe17c9687
commit
137728be1f
8 changed files with 2008 additions and 7 deletions
|
@ -203,6 +203,7 @@ Tested on:
|
|||
* HP ProLiant BL460c Gen8
|
||||
* HP ProLiant BL460c Gen9
|
||||
* HP ProLiant BL460c Gen10
|
||||
* HP ProLiant BL460c Gen10 Graphics Exp its expansion HP ProLiant BL460c Graphics Expansion Blade
|
||||
* HP Moonshot 1500 Enclosure (your `DeviceType` should have slots batch create with `Bay c[1-45n1]`) with HP ProLiant m750, m710x, m510 Server Cartridge
|
||||
|
||||
### Pizzas
|
||||
|
|
|
@ -121,6 +121,13 @@ class ServerBase():
|
|||
"""
|
||||
return self.system[0]['Serial Number'].strip()
|
||||
|
||||
def get_expansion_service_tag(self):
|
||||
"""
|
||||
Return the virtual Service Tag from dmidecode info host
|
||||
with 'expansion'
|
||||
"""
|
||||
return self.system[0]['Serial Number'].strip() + " expansion"
|
||||
|
||||
def get_hostname(self):
|
||||
if config.hostname_cmd is None:
|
||||
return '{}'.format(socket.gethostname())
|
||||
|
@ -153,6 +160,9 @@ class ServerBase():
|
|||
def get_power_consumption(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_expansion_product(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _netbox_create_chassis(self, datacenter, tenant, rack):
|
||||
device_type = get_device_type(self.get_chassis())
|
||||
device_role = get_device_role(config.device.chassis_role)
|
||||
|
@ -193,6 +203,28 @@ class ServerBase():
|
|||
)
|
||||
return new_blade
|
||||
|
||||
def _netbox_create_blade_expansion(self, chassis, datacenter, tenant, rack):
|
||||
device_role = get_device_role(config.device.blade_role)
|
||||
device_type = get_device_type(self.get_expansion_product())
|
||||
serial = self.get_expansion_service_tag()
|
||||
hostname = self.get_hostname() + " expansion"
|
||||
logging.info(
|
||||
'Creating expansion (serial: {serial}) {hostname} on chassis {chassis_serial}'.format(
|
||||
serial=serial, hostname=hostname, chassis_serial=chassis.serial
|
||||
))
|
||||
new_blade = nb.dcim.devices.create(
|
||||
name=hostname,
|
||||
serial=serial,
|
||||
device_role=device_role.id,
|
||||
device_type=device_type.id,
|
||||
parent_device=chassis.id,
|
||||
site=datacenter.id if datacenter else None,
|
||||
tenant=tenant.id if tenant else None,
|
||||
rack=rack.id if rack else None,
|
||||
tags=self.tags,
|
||||
)
|
||||
return new_blade
|
||||
|
||||
def _netbox_create_server(self, datacenter, tenant, rack):
|
||||
device_role = get_device_role(config.device.server_role)
|
||||
device_type = get_device_type(self.get_product_name())
|
||||
|
@ -250,6 +282,41 @@ class ServerBase():
|
|||
slot=slot
|
||||
))
|
||||
|
||||
def _netbox_set_or_update_blade_expansion_slot(self, server, chassis, datacenter):
|
||||
# before everything check if right chassis
|
||||
actual_device_bay = server.parent_device.device_bay if server.parent_device else None
|
||||
actual_chassis = actual_device_bay.device if actual_device_bay else None
|
||||
slot = self.get_blade_expansion_slot()
|
||||
if actual_chassis and \
|
||||
actual_chassis.serial == chassis.serial and \
|
||||
actual_device_bay.name == slot:
|
||||
return
|
||||
|
||||
server.name += " expansion"
|
||||
|
||||
real_device_bays = nb.dcim.device_bays.filter(
|
||||
device_id=chassis.id,
|
||||
name=slot,
|
||||
)
|
||||
if len(real_device_bays) > 0:
|
||||
logging.info(
|
||||
'Setting device expansion ({serial}) new slot on {slot} '
|
||||
'(Chassis {chassis_serial})..'.format(
|
||||
serial=server.serial, slot=slot, chassis_serial=chassis.serial
|
||||
))
|
||||
# reset actual device bay if set
|
||||
if actual_device_bay:
|
||||
actual_device_bay.installed_device = None
|
||||
actual_device_bay.save()
|
||||
# setup new device bay
|
||||
real_device_bay = real_device_bays[0]
|
||||
real_device_bay.installed_device = server
|
||||
real_device_bay.save()
|
||||
else:
|
||||
logging.error('Could not find slot {slot} expansion for chassis'.format(
|
||||
slot=slot
|
||||
))
|
||||
|
||||
def netbox_create_or_update(self, config):
|
||||
"""
|
||||
Netbox method to create or update info about our server/blade
|
||||
|
@ -300,6 +367,15 @@ class ServerBase():
|
|||
self.power.create_or_update_power_supply()
|
||||
self.power.report_power_consumption()
|
||||
|
||||
if self.own_expansion_slot():
|
||||
logging.debug('Update Server expansion...')
|
||||
expansion = nb.dcim.devices.get(serial=self.get_expansion_service_tag())
|
||||
if not expansion:
|
||||
expansion = self._netbox_create_blade_expansion(chassis, datacenter, tenant, rack)
|
||||
|
||||
# set slot for blade expansion
|
||||
self._netbox_set_or_update_blade_expansion_slot(expansion, chassis, datacenter)
|
||||
|
||||
update = 0
|
||||
# for every other specs
|
||||
# check hostname
|
||||
|
@ -326,6 +402,7 @@ class ServerBase():
|
|||
print('Rack:', self.get_rack())
|
||||
print('Netbox Rack:', self.get_netbox_rack())
|
||||
print('Is blade:', self.is_blade())
|
||||
print('Got expansion:', self.own_expansion_slot())
|
||||
print('Product Name:', self.get_product_name())
|
||||
print('Chassis:', self.get_chassis())
|
||||
print('Chassis service tag:', self.get_chassis_service_tag())
|
||||
|
|
26
netbox_agent/vendors/dell.py
vendored
26
netbox_agent/vendors/dell.py
vendored
|
@ -67,3 +67,29 @@ class DellHost(ServerBase):
|
|||
break
|
||||
|
||||
return value
|
||||
|
||||
def get_expansion_product(self):
|
||||
"""
|
||||
Get the extension slot that is on a pair slot number
|
||||
next to the compute slot that is on an odd slot number
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_expansion_slot(self, server):
|
||||
"""
|
||||
Return True if its an extension slot
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_blade_expansion_slot(self):
|
||||
"""
|
||||
Expansion slot are always the compute bay number + 1
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def own_expansion_slot(self):
|
||||
"""
|
||||
Say if the device can host an extension card based
|
||||
on the product name
|
||||
"""
|
||||
pass
|
||||
|
|
26
netbox_agent/vendors/generic.py
vendored
26
netbox_agent/vendors/generic.py
vendored
|
@ -21,3 +21,29 @@ class GenericHost(ServerBase):
|
|||
|
||||
def get_chassis_service_tag(self):
|
||||
return self.get_service_tag()
|
||||
|
||||
def get_expansion_product(self):
|
||||
"""
|
||||
Get the extension slot that is on a pair slot number
|
||||
next to the compute slot that is on an odd slot number
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_expansion_slot(self, server):
|
||||
"""
|
||||
Return True if its an extension slot
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_blade_expansion_slot(self):
|
||||
"""
|
||||
Expansion slot are always the compute bay number + 1
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def own_expansion_slot(self):
|
||||
"""
|
||||
Say if the device can host an extension card based
|
||||
on the product name
|
||||
"""
|
||||
pass
|
||||
|
|
44
netbox_agent/vendors/hp.py
vendored
44
netbox_agent/vendors/hp.py
vendored
|
@ -11,12 +11,9 @@ class HPHost(ServerBase):
|
|||
self.hp_rack_locator = self._find_rack_locator()
|
||||
|
||||
def is_blade(self):
|
||||
if self.product.startswith("ProLiant BL"):
|
||||
return True
|
||||
elif self.product.startswith("ProLiant m") and self.product.endswith("Server Cartridge"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
blade = self.product.startswith("ProLiant BL")
|
||||
blade |= self.product.startswith("ProLiant m") and self.product.endswith("Server Cartridge")
|
||||
return blade
|
||||
|
||||
def _find_rack_locator(self):
|
||||
"""
|
||||
|
@ -27,7 +24,7 @@ class HPHost(ServerBase):
|
|||
# FIXME: make a dmidecode function get_by_dminame() ?
|
||||
if self.is_blade():
|
||||
locator = dmidecode.get_by_type(self.dmi, 204)
|
||||
if self.product == "ProLiant BL460c Gen10":
|
||||
if self.product.startswith("ProLiant BL460c Gen10"):
|
||||
locator = locator[0]["Strings"]
|
||||
return {
|
||||
"Enclosure Model": locator[2].strip(),
|
||||
|
@ -68,3 +65,36 @@ class HPHost(ServerBase):
|
|||
if self.is_blade():
|
||||
return self.hp_rack_locator["Enclosure Serial"].strip()
|
||||
return self.get_service_tag()
|
||||
|
||||
def get_expansion_product(self):
|
||||
"""
|
||||
Get the extension slot that is on a pair slot number
|
||||
next to the compute slot that is on an odd slot number
|
||||
I only know on model of slot GPU extension card that.
|
||||
"""
|
||||
if self.own_expansion_slot():
|
||||
return "ProLiant BL460c Graphics Expansion Blade"
|
||||
return None
|
||||
|
||||
def is_expansion_slot(self, server):
|
||||
"""
|
||||
Return True if its an extension slot, based on the name
|
||||
"""
|
||||
return server.name.endswith(" expansion")
|
||||
|
||||
def get_blade_expansion_slot(self):
|
||||
"""
|
||||
Expansion slot are always the compute bay number + 1
|
||||
"""
|
||||
if self.is_blade() and self.own_expansion_slot():
|
||||
return 'Bay {}'.format(
|
||||
str(int(self.hp_rack_locator['Server Bay'].strip()) + 1)
|
||||
)
|
||||
return None
|
||||
|
||||
def own_expansion_slot(self):
|
||||
"""
|
||||
Say if the device can host an extension card based
|
||||
on the product name
|
||||
"""
|
||||
return self.get_product_name().endswith('Graphics Exp')
|
||||
|
|
27
netbox_agent/vendors/supermicro.py
vendored
27
netbox_agent/vendors/supermicro.py
vendored
|
@ -63,3 +63,30 @@ class SupermicroHost(ServerBase):
|
|||
if not self.is_blade():
|
||||
return None
|
||||
return 'Chassis {}'.format(self.get_chassis_service_tag())
|
||||
|
||||
def get_expansion_product(self):
|
||||
"""
|
||||
Get the extension slot that is on a pair slot number
|
||||
next to the compute slot that is on an odd slot number
|
||||
I only know on model of slot GPU extension card that.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_expansion_slot(self, server):
|
||||
"""
|
||||
Return True if its an extension slot, based on the name
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_blade_expansion_slot(self):
|
||||
"""
|
||||
Expansion slot are always the compute bay number + 1
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def own_expansion_slot(self):
|
||||
"""
|
||||
Say if the device can host an extension card based
|
||||
on the product name
|
||||
"""
|
||||
pass
|
||||
|
|
1798
tests/fixtures/dmidecode/HP_ProLiant_BL460c_Gen10_Graphics_Exp
vendored
Normal file
1798
tests/fixtures/dmidecode/HP_ProLiant_BL460c_Gen10_Graphics_Exp
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,7 @@ def test_init(fixture):
|
|||
'HP_BL460c_Gen9',
|
||||
'HP_DL380p_Gen8',
|
||||
'HP_SL4540_Gen8'
|
||||
'HP_ProLiant_BL460c_Gen10_Graphics_Exp'
|
||||
])
|
||||
def test_hp_service_tag(fixture):
|
||||
dmi = parse(fixture)
|
||||
|
@ -36,6 +37,7 @@ def test_moonshot_blade(fixture):
|
|||
assert server.get_service_tag() == 'CN66480BLA'
|
||||
assert server.get_chassis_service_tag() == 'CZ3702MD5K'
|
||||
assert server.is_blade() is True
|
||||
assert server.own_expansion_slot() is False
|
||||
|
||||
|
||||
@parametrize_with_fixtures(
|
||||
|
@ -89,3 +91,17 @@ def test_generic_host_product_name(fixture):
|
|||
dmi = parse(fixture)
|
||||
server = ServerBase(dmi)
|
||||
assert server.get_product_name() == 'SR'
|
||||
|
||||
|
||||
@parametrize_with_fixtures(
|
||||
'dmidecode/', only_filenames=[
|
||||
'HP_ProLiant_BL460c_Gen10_Graphics_Exp'
|
||||
])
|
||||
def test_hp_blade_with_gpu_expansion(fixture):
|
||||
dmi = parse(fixture)
|
||||
server = HPHost(dmi)
|
||||
assert server.get_service_tag() == '4242'
|
||||
assert server.get_chassis_service_tag() == '4343'
|
||||
assert server.is_blade() is True
|
||||
assert server.own_expansion_slot() is True
|
||||
assert server.get_expansion_service_tag() == '4242 expansion'
|
||||
|
|
Loading…
Reference in a new issue