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:
Cyrinux 2020-09-18 12:29:17 +02:00 committed by GitHub
parent 0fe17c9687
commit 137728be1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 2008 additions and 7 deletions

View file

@ -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

View file

@ -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())

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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'