Files
python-scciclient/scciclient/irmc/viom/elcm.py
Takashi Kajinami c3ca521025 Remove six
Python 2 reached its EOL long time ago so we no longer need to use six
to guarantee compatibility with Python 2. Also set the minimum python
version to avoid installation in old Python 3 versions which already
reached EOL.

Change-Id: I8f3fb493fd09943660fdae24bf592fbc3ff7ea2b
2025-02-26 13:01:52 +00:00

953 lines
28 KiB
Python

# Copyright 2017 FUJITSU LIMITED
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import json
import time
from scciclient.irmc import elcm
from scciclient.irmc import scci
PROFILE_NAME = 'AdapterConfigIrmc'
PARAM_PATH = 'Server/AdapterConfigIrmc'
class ELCMVIOMClient(object):
"""Client calling eLCM REST APIs for VIOM feature"""
def __init__(self, irmc_info):
self.irmc_info = irmc_info
def _wait_session(self, session_id, timeout=1800):
session_expiration = time.time() + timeout
while True:
resp = elcm.elcm_session_get_status(self.irmc_info, session_id)
status = resp['Session']['Status']
if status == 'running' or status == 'activated':
# Sleep a bit
time.sleep(5)
elif status == 'terminated regularly':
return {}
else:
# Error occurred, get session log to see what happened
try:
session_log = elcm.elcm_session_get_log(
irmc_info=self.irmc_info, session_id=session_id)
except scci.SCCIClientError as e:
raise scci.SCCIClientError(
('Operation Failed. Session %(session_id)s state is '
'%(session_state)s. Session log collection failed: '
'%(reason)s' %
{'session_id': session_id,
'session_state': resp['Session']['Status'],
'reason': e}))
raise scci.SCCIClientError(
('Operation failed. Session %(session_id)s state is '
'%(session_state)s. Session log is: "%(session_log)s".' %
{'session_id': session_id,
'session_state': resp['Session']['Status'],
'session_log': json.dumps(session_log)}))
# Check for timeout
if time.time() > session_expiration:
# Timeout occurred, get session log to see what happened
try:
session_log = elcm.elcm_session_get_log(
irmc_info=self.irmc_info, session_id=session_id)
except scci.SCCIClientError as e:
raise elcm.ELCMSessionTimeout(
'Operation timed out. Session %(session_id)s has not '
'finished in %(timeout)d seconds. Session log '
'collection failed: %(reason)s' %
{'session_id': session_id,
'timeout': timeout,
'reason': e})
raise elcm.ELCMSessionTimeout(
'Operation timed out. Session %(session_id)s has not '
'finished in %(timeout)d seconds. Session log is: '
'"%(session_log)s.' %
{'session_id': session_id,
'timeout': timeout,
'session_log': json.dumps(session_log)})
def set_profile(self, adapter_config):
_adapter_config = dict(adapter_config)
_adapter_config.update({'@Processing': 'execute'})
req = {'Server': {'AdapterConfigIrmc': _adapter_config,
'@Version': '1.01'}}
resp = elcm.elcm_profile_set(self.irmc_info, req)
self._wait_session(resp['Session']['Id'])
def get_profile(self):
# delete old one
try:
elcm.elcm_profile_delete(self.irmc_info, PROFILE_NAME)
except elcm.ELCMProfileNotFound:
pass
resp = elcm.elcm_profile_create(self.irmc_info, PARAM_PATH)
self._wait_session(resp['Session']['Id'])
resp = elcm.elcm_profile_get(self.irmc_info, PROFILE_NAME)
return resp
class VIOMAttribute(object):
"""Attribute in VIOM Element.
This class is used for conversion between Python class and JSON table.
"""
def __init__(self, name, key, init=None):
self.name = name
self.key = key
self.init = init
class VIOMElement(object, metaclass=abc.ABCMeta):
"""Element in VIOM table."""
def __init__(self, **kwargs):
for attr in self.__class__._BASIC_ATTRIBUTES:
setattr(self, attr.name, kwargs.get(attr.name, attr.init))
def get_basic_json(self):
table = {}
for attr in self.__class__._BASIC_ATTRIBUTES:
value = getattr(self, attr.name)
if value is not None:
table[attr.key] = value
return table
class VIOMTable(VIOMElement):
"""Root class of VIOM table"""
_BASIC_ATTRIBUTES = [
VIOMAttribute('use_virtual_addresses', 'UseVirtualAddresses'),
VIOMAttribute('viom_boot_enable', 'VIOMBootEnable'),
VIOMAttribute('boot_menu_enable', 'BootMenuEnable'),
VIOMAttribute('sriov', 'SRIOV'),
VIOMAttribute('smux', 'Smux'),
VIOMAttribute('boot_mode', 'BootMode'),
VIOMAttribute('init_boot', 'InitBoot'),
VIOMAttribute('processing', '@Processing'),
VIOMAttribute('mode', 'Mode'),
VIOMAttribute('version', '@Version', '1.00'),
]
def __init__(self, **kwargs):
super(VIOMTable, self).__init__(**kwargs)
self.slots = {}
self.manage = None
def get_slot(self, slot_idx, create=True):
slot = self.slots.get(slot_idx)
if slot or not create:
return slot
slot = Slot(slot_idx)
self.slots[slot_idx] = slot
return slot
def set_manage_table(self, manage):
self.manage = manage
def get_json(self):
"""Create JSON data for AdapterConfig.
:returns: JSON data as follows:
{
"VIOMManage":{
},
"InitBoot":{
},
"UseVirtualAddresses":{
},
"BootMenuEnable":{
},
"SmuxSetting":{
},
"Slots":{
}
}
"""
viom_table = self.get_basic_json()
if self.slots:
viom_table['Slots'] = {
'Slot': [s.get_json() for s in self.slots.values()]
}
if self.manage:
viom_table['VIOMManage'] = self.manage.get_json()
return viom_table
class ManageTable(VIOMElement):
"""Class for ViomManage element."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('manage', 'Manage'),
VIOMAttribute('identification', 'Identification'),
VIOMAttribute('trap_destination', 'TrapDestination'),
VIOMAttribute('force', 'Force'),
VIOMAttribute('preferred_version', 'PreferredInventoryVersion')
]
def __init__(self, **kwargs):
super(ManageTable, self).__init__(**kwargs)
def get_json(self):
"""Create JSON data for ViomManage.
:returns: JSON data for ViomManage as follows:
{
"Manage":{
},
"Force":{
},
"Identification":{
},
"TrapDestination":{
},
"PreferredInventoryVersion":{
}
}
"""
return self.get_basic_json()
class Slot(VIOMElement):
"""Class for Slot element."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('slot_idx', '@SlotIdx', 0),
]
def __init__(self, slot_idx, **kwargs):
super(Slot, self).__init__(slot_idx=slot_idx, **kwargs)
self.onboard_cards = {}
self.addon_cards = {}
def add_card(self, card):
if isinstance(card, OnboardCard):
self.onboard_cards[card.card_idx] = card
else:
self.addon_cards[card.card_idx] = card
def get_onboard_card(self, card_idx):
return self.onboard_cards.get(card_idx)
def get_addon_card(self, card_idx):
return self.addon_cards.get(card_idx)
def get_json(self):
"""Create JSON data for slot.
:returns: JSON data for slot as follows:
{
"@SlotIdx":0,
"OnboardControllers":{
"OnboardController": [
]
},
"AddOnCards":{
"AddOnCard": [
]
}
}
"""
json = self.get_basic_json()
if self.onboard_cards:
json['OnboardControllers'] = {
'OnboardController':
[c.get_json() for c in self.onboard_cards.values()]
}
if self.addon_cards:
json['AddOnCards'] = {
'AddOnCard': [c.get_json() for c in self.addon_cards.values()]
}
return json
class PCICard(object, metaclass=abc.ABCMeta):
"Abstract class for PCI cards."
def __init__(self, card_idx, adapter):
self.card_idx = card_idx
self.adapter = adapter
def add_port(self, port):
self.adapter.add_port(port)
def get_port(self, port_idx):
return self.adapter.get_port(port_idx)
def get_json(self):
"""Create JSON data for PCI card element.
:returns: JSON data for PCI card.
Data for onboard card is as follows:
{
"@OnboardControllerIdx":1,
"LANAdapter":{
},
"CNAAdapter":{
}
}
Data for add-on card is as follows:
{
"@AddOnCardIdx":1,
"LANAdapter":{
},
"FCAdapter":{
},
"CNAAdapter":{
}
}
"""
json = {self.INDEX_KEY: self.card_idx}
json.update(self.adapter.get_json())
return json
class OnboardCard(PCICard):
"""Class for onboard Card."""
INDEX_KEY = '@OnboardControllerIdx'
class AddOnCard(PCICard):
"""Class for add on card."""
INDEX_KEY = '@AddOnCardIdx'
class Adapter(object, metaclass=abc.ABCMeta):
"""Abstract class for adapters.
Adapter represents type of PCI card.
"""
def __init__(self):
self.ports = {}
def add_port(self, port):
self.ports[port.port_idx] = port
def get_port(self, port_idx):
return self.ports.get(port_idx)
def get_json(self):
"""Create JSON data for adapter
:returns: JSON data for adapter as follows:
{
"LANAdapter":{
"Ports":{
"Port": [
]
}
}
}
"""
return {
self.ADAPTER_NAME: {
'Ports': {
'Port': [p.get_json() for p in self.ports.values()]
}
}
}
class LANAdapter(Adapter):
"""LAN adapter."""
ADAPTER_NAME = 'LANAdapter'
class FCAdapter(Adapter):
"""FC adapter."""
ADAPTER_NAME = 'FCAdapter'
class CNAAdapter(Adapter):
"""CNA adatper."""
ADAPTER_NAME = 'CNAAdapter'
class AdapterPort(VIOMElement, metaclass=abc.ABCMeta):
"""Port in adapters."""
def __init__(self, port_idx, **kwargs):
super(AdapterPort, self).__init__(port_idx=port_idx, **kwargs)
class LANPort(AdapterPort):
"""LAN Port."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('port_idx', '@PortIdx', 1),
VIOMAttribute('port_enable', 'PortEnable'),
VIOMAttribute('sriov', 'SRIOV'),
VIOMAttribute('use_virtual_addresses', 'UseVirtualAddresses'),
]
def __init__(self, port_idx, port_enable=True, mac=None, boot=None,
**kwargs):
super(LANPort, self).__init__(port_idx, port_enable=port_enable,
**kwargs)
self.mac = mac
self.boot = boot if boot else NoneBoot()
def get_json(self):
"""Create JSON data for LANPort.
:returns: JSON data as follows:
{
"@PortIdx":1,
"PortEnable":{
},
"UseVirtualAddresses":{
},
"BootProtocol":{
},
"VirtualAddress":{
"MAC":{
}
},
"BootPriority":{
},
"ISCSIBootEnvironment":{
}
}
"""
port = self.get_basic_json()
port.update({
'BootProtocol': self.boot.BOOT_PROTOCOL,
'BootPriority': self.boot.boot_prio,
})
boot_env = self.boot.get_json()
if boot_env:
port.update(boot_env)
if self.use_virtual_addresses and self.mac:
port['VirtualAddress'] = {'MAC': self.mac}
return port
class FCPort(AdapterPort):
"""FC Port."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('port_idx', '@PortIdx', 1),
VIOMAttribute('port_enable', 'PortEnable'),
VIOMAttribute('sriov', 'SRIOV'),
VIOMAttribute('use_virtual_addresses', 'UseVirtualAddresses'),
]
def __init__(self, port_idx, port_enable=True, wwnn=None, wwpn=None,
boot=None, **kwargs):
super(FCPort, self).__init__(port_idx, port_enable=port_enable,
**kwargs)
self.wwnn = wwnn
self.wwpn = wwpn
self.boot = boot if boot else NoneBoot()
def get_json(self):
"""Create FC port.
:returns: JSON for FC port as follows:
{
"@PortIdx":1,
"PortEnable":{
},
"UseVirtualAddresses":{
},
"VirtualAddress":{
"WWNN":{
},
"WWPN":{
},
"MAC":{
}
},
"BootProtocol":{
},
"BootPriority":{
},
"FCBootEnvironment":{
}
}
"""
port = self.get_basic_json()
port.update({
'BootProtocol': self.boot.BOOT_PROTOCOL,
'BootPriority': self.boot.boot_prio,
})
boot_env = self.boot.get_json()
if boot_env:
port.update(boot_env)
if self.use_virtual_addresses:
addresses = {}
if self.wwnn:
addresses['WWNN'] = self.wwnn
if self.wwpn:
addresses['WWPN'] = self.wwpn
if addresses:
port['VirtualAddress'] = addresses
return port
class CNAPort(AdapterPort):
"""CNA port."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('port_idx', '@PortIdx', 1),
VIOMAttribute('port_enable', 'PortEnable'),
]
def __init__(self, port_idx, port_enable=True):
super(CNAPort, self).__init__(port_idx, port_enable=port_enable)
self.functions = {}
def add_function(self, function):
self.functions[function.func_idx] = function
def get_function(self, func_idx):
return self.functions.get(func_idx)
def get_json(self):
"""Create JSON for CNA port.
:returns: JSON for CNA port as follows:
{
"@PortIdx":1,
"PortEnable":{
},
"Functions":{
}
}
"""
port = self.get_basic_json()
port['Functions'] = {
'Function': [f.get_json() for f in self.functions.values()]
}
return port
class CNAFunction(VIOMElement, metaclass=abc.ABCMeta):
"""Abstract class for Functions for CNA card"""
_BASIC_ATTRIBUTES = [
VIOMAttribute('function_enable', 'FunctionEnable'),
VIOMAttribute('vlan_id', 'VLANId'),
VIOMAttribute('sriov', 'SRIOV'),
VIOMAttribute('use_virtual_addresses', 'UseVirtualAddresses'),
VIOMAttribute('bandwidth', 'Bandwidth'),
VIOMAttribute('rate_limit', 'RateLimit'),
]
def __init__(self, func_idx, function_enable=True, boot=None, **kwargs):
super(CNAFunction, self).__init__(**kwargs)
self.func_idx = func_idx
self.boot = boot if boot else NoneBoot()
self.function_enable = function_enable
def _get_virtual_addresses_json(self, json):
return None
def get_json(self):
"""Create JSON for CNA function.
:returns: JSON for CNA function.
* LANFunction creates the following JSON:
{
"LANFunction":{
"FunctionEnable":{
},
"BootProtocol":{
},
"UseVirtualAddresses":{
},
"BootPriority":{
},
"Bandwidth":{
},
"RateLimit":{
},
"VLANId":{
},
"VirtualAddress":{
"MAC":{
}
}
}
}
* FCoEFunction creates the following JSON:
{
"FCoEFunction":{
"FunctionEnable":{
},
"BootProtocol":{
},
"UseVirtualAddresses":{
},
"BootPriority":{
},
"Bandwidth":{
},
"RateLimit":{
},
"VLANId":{
},
"VirtualAddress":{
"WWNN":{
},
"WWPN":{
},
"MAC":{
}
},
"FCBootEnvironment":{
}
}
}
* ISCSIFunction creates the following JSON:
{
"@FunctionIdx": 1,
"ISCSIFunction":{
"FunctionEnable":{
},
"BootProtocol":{
},
"UseVirtualAddresses":{
},
"BootPriority":{
},
"Bandwidth":{
},
"RateLimit":{
},
"VLANId":{
},
"VirtualAddress":{
"MAC":{
}
},
"ISCSIBootEnvironment":{
}
}
}
"""
function = self.get_basic_json()
function['BootProtocol'] = self.boot.BOOT_PROTOCOL
function['BootPriority'] = self.boot.boot_prio
if self.use_virtual_addresses:
virtual_addresses = self._get_virtual_addresses_json()
if virtual_addresses:
function['VirtualAddress'] = virtual_addresses
boot_env = self.boot.get_json()
if boot_env:
function.update(boot_env)
return {'@FunctionIdx': self.func_idx,
self.FUNCTION_NAME: function}
class LANFunction(CNAFunction):
"""LAN function for CNA card"""
FUNCTION_NAME = 'LANFunction'
def __init__(self, func_idx, function_enable=True, boot=None, mac=None,
**kwargs):
super(LANFunction, self).__init__(
func_idx, function_enable=function_enable, boot=boot, **kwargs)
self.mac = mac
def _get_virtual_addresses_json(self):
return {'MAC': self.mac} if self.mac else None
class FCoEFunction(CNAFunction):
"""FCoE Function for CNA card."""
FUNCTION_NAME = 'FCoEFunction'
def __init__(self, func_idx, function_enable=True, boot=None, wwnn=None,
wwpn=None, mac=None, **kwargs):
super(FCoEFunction, self).__init__(
func_idx, function_enable=function_enable, boot=boot, **kwargs)
self.wwnn = wwnn
self.wwpn = wwpn
self.mac = mac
def _get_virtual_addresses_json(self):
virtual_addresses = {}
if self.mac:
virtual_addresses['MAC'] = self.mac
if self.wwnn:
virtual_addresses['WWNN'] = self.wwnn
if self.wwpn:
virtual_addresses['WWPN'] = self.wwpn
return virtual_addresses
class ISCSIFunction(CNAFunction):
"""iSCSI Function for CNA card."""
FUNCTION_NAME = 'ISCSIFunction'
def __init__(self, func_idx, function_enable=True, boot=None, mac=None,
**kwargs):
super(ISCSIFunction, self).__init__(
func_idx, function_enable=function_enable, boot=boot, **kwargs)
self.mac = mac
def _get_virtual_addresses_json(self):
return {'MAC': self.mac} if self.mac else None
class Boot(VIOMElement, metaclass=abc.ABCMeta):
"""Abstract class for BootProtocol"""
_BASIC_ATTRIBUTES = []
def __init__(self, boot_prio=1, **kwargs):
super(Boot, self).__init__(**kwargs)
self.boot_prio = boot_prio
def get_json(self):
return {}
class NoneBoot(Boot):
"""None BootProtocol."""
BOOT_PROTOCOL = 'None'
class PXEBoot(Boot):
"""PXE BootProtocol."""
BOOT_PROTOCOL = 'PXE'
class FCBoot(Boot):
"""FC BootProtocol with FCBootEnvironment elemnt."""
BOOT_PROTOCOL = 'FC'
_BASIC_ATTRIBUTES = [
VIOMAttribute('link_speed', 'FCLinkSpeed', 'auto'),
VIOMAttribute('topology', 'FCTopology', 'auto_loop'),
VIOMAttribute('boot_enable', 'SANBootEnable'),
]
def __init__(self, boot_prio=1, **kwargs):
super(FCBoot, self).__init__(boot_prio, **kwargs)
self.targets = []
def add_target(self, target):
self.targets.append(target)
def get_json(self):
"""Create JSON for FCBootEnvironment.
:returns: JSON for FCBootEnvironment as follows:
{
"FCBootEnvironment":{
"FCTargets":{
"FCTarget":[
]
},
"FCLinkSpeed":{
},
"SANBootEnable":{
},
"FCTopology":{
}
}
}
"""
json = self.get_basic_json()
for i in range(len(self.targets)):
# @FCTargetIdx starts from 1.
self.targets[i].set_index(i + 1)
json['FCTargets'] = {
'FCTarget': [t.get_json() for t in self.targets]
}
return {'FCBootEnvironment': json}
class FCTarget(VIOMElement):
"""FC Target."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('index', '@FCTargetIdx', 1),
VIOMAttribute('wwpn', 'TargetWWPN'),
VIOMAttribute('lun', 'TargetLUN')
]
def __init__(self, wwpn, lun=0, **kwargs):
super(FCTarget, self).__init__(wwpn=wwpn, lun=lun)
def set_index(self, index):
self.index = index
def get_json(self):
"""Create JSON for FCTarget.
:returns: JSON data for FCTarget as follows:
{
"@FCTargetIdx":1,
"TargetWWPN":{
},
"TargetLUN":{
}
}
"""
return self.get_basic_json()
class ISCSIBoot(Boot):
"""iSCSI BootProtocol with ISCSIBootEnvironment elment."""
BOOT_PROTOCOL = 'ISCSI'
def __init__(self, initiator, target, boot_prio=1):
super(ISCSIBoot, self).__init__(boot_prio)
self.initiator = initiator
self.target = target
def get_json(self):
"""Create JSON for ISCSIBoot.
:returns: JSON data for ISCSIBoot as follows:
{
"ISCSIBootEnvironment":{
"ISCSIInitiator":{
},
"ISCSITarget":{
}
}
}
"""
return {
'ISCSIBootEnvironment': {
'ISCSIInitiator': self.initiator.get_json(),
'ISCSITarget': self.target.get_json()
}
}
class ISCSIInitiator(VIOMElement):
"""iSCSIInitiator."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('dhcp_usage', 'DHCPUsage', False),
VIOMAttribute('iqn', 'Name'),
VIOMAttribute('ip', 'IPv4Address'),
VIOMAttribute('subnet', 'SubnetMask'),
VIOMAttribute('gateway', 'GatewayIPv4Address'),
VIOMAttribute('vlan_id', 'VLANId', 0),
]
def __init__(self, **kwargs):
super(ISCSIInitiator, self).__init__(**kwargs)
def get_json(self):
"""Create JSON data for iSCSI initiator.
:returns: JSON data for iSCSI initiator as follows:
{
"DHCPUsage":{
},
"Name":{
},
"IPv4Address":{
},
"SubnetMask":{
},
"GatewayIPv4Address":{
},
"VLANId":{
}
}
"""
if self.dhcp_usage:
return {'DHCPUsage': self.dhcp_usage,
'Name': self.iqn}
else:
return self.get_basic_json()
class ISCSITarget(VIOMElement):
"""iSCSI target."""
_BASIC_ATTRIBUTES = [
VIOMAttribute('dhcp_usage', 'DHCPUsage', False),
VIOMAttribute('iqn', 'Name'),
VIOMAttribute('ip', 'IPv4Address'),
VIOMAttribute('port', 'PortNumber', 3260),
VIOMAttribute('lun', 'BootLUN', 0),
VIOMAttribute('auth_method', 'AuthenticationMethod', 'None'),
VIOMAttribute('chap_user', 'ChapUserName'),
VIOMAttribute('chap_secret', 'ChapSecret'),
VIOMAttribute('mutual_chap_secret', 'MutualChapSecret'),
]
def __init__(self, **kwargs):
super(ISCSITarget, self).__init__(**kwargs)
def get_json(self):
"""Create JSON data for iSCSI target.
:returns: JSON data for iSCSI target as follows:
{
"DHCPUsage":{
},
"Name":{
},
"IPv4Address":{
},
"PortNumber":{
},
"BootLUN":{
},
"AuthenticationMethod":{
},
"ChapUserName":{
},
"ChapSecret":{
},
"MutualChapSecret":{
}
}
"""
json = {
'DHCPUsage': self.dhcp_usage,
'AuthenticationMethod': self.auth_method,
}
if not self.dhcp_usage:
json['Name'] = self.iqn
json['IPv4Address'] = self.ip
json['PortNumber'] = self.port
json['BootLUN'] = self.lun
if self.chap_user:
json['ChapUserName'] = self.chap_user
if self.chap_secret:
json['ChapSecret'] = self.chap_secret
if self.mutual_chap_secret:
json['MutualChapSecret'] = self.mutual_chap_secret
return json