Merge "Add support for additional network resources."

This commit is contained in:
Zuul
2021-11-24 08:16:22 +00:00
committed by Gerrit Code Review
13 changed files with 986 additions and 1 deletions

View File

@@ -16,11 +16,14 @@
from sushy.resources import base
from sushy.resources import common
from sushy.resources.system.network import device_function
from sushy.resources.system.network import port
from sushy import utils
class NetworkAdapter(base.ResourceBase):
description = base.Field('Description')
"""Human-readable description of the BIOS resource"""
"""Human-readable description of the resource"""
identity = base.Field('Id', required=True)
"""The network adapter identity string"""
@@ -43,6 +46,45 @@ class NetworkAdapter(base.ResourceBase):
status = common.StatusField("Status")
"""The status"""
@property
@utils.cache_it
def network_device_functions(self):
"""Property to reference `NetworkDeviceFunctionCollection` instance
It is set once when the first time it is queried. On refresh,
this property is marked as stale (greedy-refresh not done).
Here the actual refresh of the sub-resource happens, if stale.
"""
return device_function.NetworkDeviceFunctionCollection(
self._conn,
path=utils.get_sub_resource_path_by(
self,
"NetworkDeviceFunctions"
),
redfish_version=self.redfish_version,
registries=self.registries,
root=self.root
)
@property
@utils.cache_it
def network_ports(self):
"""Property to reference `NetworkPortCollection` instance
It is set once when the first time it is queried. On refresh,
this property is marked as stale (greedy-refresh not done).
Here the actual refresh of the sub-resource happens, if stale.
"""
return port.NetworkPortCollection(
self._conn,
path=utils.get_sub_resource_path_by(self, "NetworkPorts"),
redfish_version=self.redfish_version,
registries=self.registries,
root=self.root
)
class NetworkAdapterCollection(base.ResourceCollectionBase):

View File

@@ -0,0 +1,89 @@
# Copyright (c) 2021 Anexia Internetdienstleistungs GmbH
# 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.
# AuthenticationMethod Types
AUTHENTICATION_METHOD_CHAP = 'iSCSI Challenge Handshake'\
'Authentication Protocol'
"""
iSCSI Challenge Handshake Authentication Protocol (CHAP)
authentication is used.
"""
AUTHENTICATION_METHOD_MUTUAL_CHAP = 'iSCSI Mutual Challenge Handshake'\
'Authentication Protocol'
"""
iSCSI Mutual Challenge Handshake Authentication Protocol (CHAP)
authentication is used.
"""
AUTHENTICATION_METHOD_NONE = 'none'
"""No iSCSI authentication is used."""
# BootMode Types
BOOT_MODE_DISABLED = 'disabled'
"""Do not indicate to UEFI/BIOS that this device is bootable."""
BOOT_MODE_PXE = 'pxe'
"""Boot this device by PXE"""
# IP Address Types
IP_ADDRESS_TYPE_IPV4 = 'IPv4'
"""IPv4 addressing is used for all IP-fields in this object."""
IP_ADDRESS_TYPE_IPV6 = 'IPv6'
"""IPv6 addressing is used for all IP-fields in this object."""
# FlowControl Types
FLOW_CONTROL_NONE = 'none'
"""No IEEE 802.3x flow control is enabled on this port."""
FLOW_CONTROL_RX = 'rx'
"""IEEE 802.3x flow control may be initiated by the link partner."""
FLOW_CONTROL_TX = 'tx'
"""IEEE 802.3x flow control may be initiated by this station."""
FLOW_CONTROL_TX_RX = 'tx/rx'
"""
IEEE 802.3x flow control may be initiated
by this station or the link partner.
"""
# NetworkDeviceTechnology Types
NETWORK_DEVICE_TECHNOLOGY_DISABLED = 'disabled'
"""Neither enumerated nor visible to the operating system."""
NETWORK_DEVICE_TECHNOLOGY_ETHERNET = 'ethernet'
"""Appears to the operating system as an Ethernet device."""
NETWORK_DEVICE_TECHNOLOGY_FIBRE_CHANNEL = 'fibre channel'
"""Appears to the operating system as a Fibre Channel device."""
NETWORK_DEVICE_TECHNOLOGY_FCOE = 'fibre channel over ethernet'
"""Appears to the operating system as an FCoE device."""
NETWORK_DEVICE_TECHNOLOGY_ISCSI = 'Internet SCSI'
"""Appears to the operating system as an iSCSI device."""
# LinkStatus Types
LINK_STATUS_DOWN = 'down'
"""The port is enabled but link is down."""
LINK_STATUS_UP = 'up'
"""The port is enabled and link is good (up)."""

View File

@@ -0,0 +1,188 @@
# Copyright (c) 2021 Anexia Internetdienstleistungs GmbH
# 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.
# This is referred from Redfish standard schema.
# https://redfish.dmtf.org/schemas/v1/NetworkDeviceFunction.v1_3_3.json
from sushy.resources import base
from sushy.resources import common
from sushy.resources.system.network import mappings as net_maps
from sushy.resources.system.network import port
from sushy import utils
class BootTargetsField(base.ListField):
lun_id = base.Field("LUNID")
"""The logical unit number (LUN) ID from which to boot on the device"""
priority = base.Field("BootPriority", adapter=utils.int_or_none)
"""The relative priority for this entry in the boot targets array."""
wwpn = base.Field("WWPN")
"""The World Wide Port Name (WWPN) from which to boot."""
class VLANField(base.CompositeField):
vlan_enabled = base.Field("VLANEnable", adapter=utils.bool_or_none)
vlan_id = base.Field("VLANId", adapter=utils.int_or_none)
class ISCSIBootField(base.CompositeField):
authentication_method = base.MappedField(
'AuthenticationMethod', net_maps.AUTHENTICATION_METHOD_TYPE_MAP)
"""The configured capability of this network device function."""
initiator_default_gateway = base.Field('InitiatorDefaultGateway')
"""The IPv6 or IPv4 iSCSI boot default gateway."""
initiator_ip_address = base.Field('InitiatorIPAddress')
"""The IPv6 or IPv4 address of the iSCSI initiator."""
initiator_netmask = base.Field('InitiatorNetmask')
"""The IPv6 or IPv4 netmask of the iSCSI boot initiator."""
ip_address_type = base.MappedField(
'IPAddressType', net_maps.IP_ADDRESS_TYPE_MAP)
"""The type of IP address being populated IP address fields."""
primary_dns = base.Field('PrimaryDNS')
"""The IPv6 or IPv4 address of the primary DNS server."""
primary_lun = base.Field('PrimaryLUN', adapter=utils.int_or_none)
"""The logical unit number (LUN) for the primary iSCSI boot target."""
primary_target_ip_address = base.Field('PrimaryTargetIPAddress')
"""The IPv4 or IPv6 address for the primary iSCSI boot target."""
primary_target_tcp_port = base.Field('PrimaryTargetTCPPort')
"""The TCP port for the primary iSCSI boot target."""
primary_vlan_enabled = base.Field(
'PrimaryVLANEnable',
adapter=utils.bool_or_none
)
"""An indication of whether the primary VLAN is enabled."""
primary_vlan_id = base.Field("PrimaryVLANId", adapter=utils.int_or_none)
"""The 802.1q VLAN ID to use for iSCSI boot from the primary target."""
secondary_dns = base.Field('SecondaryDNS')
"""The IPv6 or IPv4 address of the secondary DNS server."""
secondary_lun = base.Field('SecondaryLUN', adapter=utils.int_or_none)
"""The logical unit number (LUN) for the secondary iSCSI boot target."""
secondary_target_ip_address = base.Field('SecondaryTargetIPAddress')
"""The IPv4 or IPv6 address for the secondary iSCSI boot target."""
secondary_target_tcp_port = base.Field('SecondaryTargetTCPPort')
"""The TCP port for the secondary iSCSI boot target."""
secondary_vlan_enabled = base.Field(
'SecondaryVLANEnable', adapter=utils.bool_or_none)
"""An indication of whether the secondary VLAN is enabled."""
secondary_vlan_id = base.Field(
"SecondaryVLANId",
adapter=utils.int_or_none
)
"""The 802.1q VLAN ID to use for iSCSI boot from the secondary target."""
class EthernetField(base.CompositeField):
mac_address = base.Field("MACAddress")
"""The currently configured MAC address of the resource"""
mtu_size = base.Field("MTUSize", adapter=utils.int_or_none)
"""The Maximum Transmission Unit (MTU) configured for this resource"""
permanent_mac_address = base.Field("PermanentMACAddress")
"""The permanent MAC address assigned to this resource"""
vlan = VLANField("VLAN")
"""The VLAN for this interface"""
class FibreChannelField(base.CompositeField):
boot_targets = BootTargetsField("BootTargets")
"""An array of Fibre Channel boot targets configured for this resource."""
class NetworkDeviceFunction(base.ResourceBase):
capabilities = base.MappedListField(
'NetDevFuncCapabilities', net_maps.NETWORK_TECHNOLOGY_TYPE_MAP)
"""An array of capabilities for this network device function."""
type = base.MappedField(
'NetDevFuncType', net_maps.NETWORK_TECHNOLOGY_TYPE_MAP)
"""The configured capability of this network device function."""
description = base.Field('Description')
"""The network device function description"""
ethernet = EthernetField("Ethernet")
"""The Ethernet capabilities, status, and configuration values."""
fibre_channel = FibreChannelField("FibreChannel")
"""The Fibre Channel capabilities, status, and configuration values."""
identity = base.Field('Id', required=True)
"""Identifier for the network device function"""
iscsi_boot = ISCSIBootField('iSCSIBoot')
"""
The iSCSI boot capabilities, status, and configuration
for a network device function.
"""
max_virtual_functions = base.Field(
'MaxVirtualFunctions',
adapter=utils.int_or_none
)
"""
The number of virtual functions that are available
for this network device function.
"""
name = base.Field('Name', required=True)
"""The network device function name"""
status = common.StatusField('Status')
"""The status of the resource"""
@property
@utils.cache_it
def assignable_physical_ports(self):
"""An array of physical ports to which this resource may be assigned.
Network ports to which this network device function may be assigned.
:raises: MissingAttributeError if '@odata.id' field is missing.
:returns: A list of `NetworkPort` instances
"""
paths = utils.get_sub_resource_path_by(
self, "AssignablePhysicalPorts", is_collection=True)
return [port.NetworkPort(self._conn, path,
redfish_version=self.redfish_version,
registries=self.registries,
root=self.root
)
for path in paths]
class NetworkDeviceFunctionCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return NetworkDeviceFunction

View File

@@ -0,0 +1,53 @@
# Copyright (c) 2021 Anexia Internetdienstleistungs GmbH
# 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.
from sushy.resources.system.network import constants as net_cons
AUTHENTICATION_METHOD_TYPE_MAP = {
'CHAP': net_cons.AUTHENTICATION_METHOD_CHAP,
'MutualCHAP': net_cons.AUTHENTICATION_METHOD_MUTUAL_CHAP,
'None': net_cons.AUTHENTICATION_METHOD_NONE
}
BOOT_MODE_TYPE_MAP = {
'Disabled': net_cons.BOOT_MODE_DISABLED,
'FibreChannel': net_cons.NETWORK_DEVICE_TECHNOLOGY_FIBRE_CHANNEL,
'FibreChannelOverEthernet': net_cons.NETWORK_DEVICE_TECHNOLOGY_FCOE,
'PXE': net_cons.BOOT_MODE_PXE,
'iSCSI': net_cons.NETWORK_DEVICE_TECHNOLOGY_ISCSI
}
IP_ADDRESS_TYPE_MAP = {
'IPv4': net_cons.IP_ADDRESS_TYPE_IPV4,
'IPv6': net_cons.IP_ADDRESS_TYPE_IPV6,
}
LINK_STATUS_TYPE_MAP = {
'Up': net_cons.LINK_STATUS_UP,
'Down': net_cons.LINK_STATUS_DOWN,
}
FLOW_CONTROL_TYPE_MAP = {
'None': net_cons.FLOW_CONTROL_NONE,
'TX': net_cons.FLOW_CONTROL_TX,
'RX': net_cons.FLOW_CONTROL_RX,
'TX_RX': net_cons.FLOW_CONTROL_TX_RX,
}
NETWORK_TECHNOLOGY_TYPE_MAP = {
'Disabled': net_cons.NETWORK_DEVICE_TECHNOLOGY_DISABLED,
'Ethernet': net_cons.NETWORK_DEVICE_TECHNOLOGY_ETHERNET,
'FibreChannel': net_cons.NETWORK_DEVICE_TECHNOLOGY_FIBRE_CHANNEL,
'iSCSI': net_cons.NETWORK_DEVICE_TECHNOLOGY_ISCSI,
'FibreChannelOverEthernet': net_cons.NETWORK_DEVICE_TECHNOLOGY_FCOE
}

View File

@@ -0,0 +1,71 @@
# Copyright (c) 2021 Anexia Internetdienstleistungs GmbH
# 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.
# This is referred from Redfish standard schema.
# https://redfish.dmtf.org/schemas/v1/NetworkPort.v1_2_1.json
from sushy.resources import base
from sushy.resources import common
from sushy.resources.system.network import mappings as net_maps
class NetworkPortCollection(base.ResourceCollectionBase):
@property
def _resource_type(self):
return NetworkPort
class NetworkPort(base.ResourceBase):
associated_network_addresses = base.Field(
"AssociatedNetworkAddresses", adapter=list
)
"""The array of configured network addresses that are associated."""
current_link_speed_mbps = base.Field(
"CurrentLinkSpeedMbps", adapter=int
)
"""The network port current link speed."""
description = base.Field('Description')
"""The network port description"""
flow_control_configuration = base.MappedField(
'FlowControlConfiguration',
net_maps.FLOW_CONTROL_TYPE_MAP
)
"""The locally configured 802.3x flow control setting."""
flow_control_status = base.MappedField('FlowControlStatus',
net_maps.FLOW_CONTROL_TYPE_MAP)
"""The 802.3x flow control behavior negotiated with the link partner"""
identity = base.Field('Id', required=True)
"""The network port identity"""
link_status = base.MappedField(
'LinkStatus',
net_maps.LINK_STATUS_TYPE_MAP
)
"""The link status of the network port."""
name = base.Field(
"Name", required=True
)
"""The network port name"""
physical_port_number = base.Field("PhysicalPortNumber", adapter=int)
"""The physical port number label for this port."""
status = common.StatusField('Status')
"""The network port status"""

View File

@@ -0,0 +1,83 @@
{
"@Redfish.Settings": {
"@odata.context": "/redfish/v1/$metadata#Settings.Settings",
"@odata.type": "#Settings.v1_3_0.Settings",
"SettingsObject": {
"@odata.id": "/redfish/v1/Systems/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1/Settings"
},
"SupportedApplyTimes": [
"OnReset",
"AtMaintenanceWindowStart",
"InMaintenanceWindowOnReset"
]
},
"@odata.context": "/redfish/v1/$metadata#NetworkDeviceFunction.NetworkDeviceFunction",
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1",
"@odata.type": "#NetworkDeviceFunction.v1_3_3.NetworkDeviceFunction",
"AssignablePhysicalPorts": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2"
}
],
"AssignablePhysicalPorts@odata.count": 1,
"Description": "NetworkDeviceFunction",
"Ethernet": {
"MACAddress": "01:01:01:01:01:D1",
"MTUSize": 1234,
"PermanentMACAddress": "01:01:01:01:01:D1",
"VLAN": {
"VLANEnable": true,
"VLANId": 1
}
},
"FibreChannel": {
"BootTargets": [
{
"LUNID": "1234",
"WWPN": "ABCEDFG"
}
],
"BootTargets@odata.count": 1
},
"Id": "NIC.Integrated.1-2-1",
"Links": {
"PhysicalPortAssignment": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2"
},
"Oem": {}
},
"MaxVirtualFunctions": 127,
"Name": "NetworkDeviceFunction",
"NetDevFuncCapabilities": [
"Disabled",
"Ethernet",
"iSCSI"
],
"NetDevFuncCapabilities@odata.count": 3,
"NetDevFuncType": "Ethernet",
"Oem": {},
"PhysicalPortAssignment": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2"
},
"Status": {
"State": "Enabled",
"Health": "OK",
"HealthRollup": "OK"
},
"iSCSIBoot": {
"AuthenticationMethod": "None",
"IPAddressType": "IPv4",
"InitiatorDefaultGateway": "0.0.0.0",
"InitiatorIPAddress": "0.0.0.0",
"InitiatorNetmask": "0.0.0.0",
"PrimaryDNS": "0.0.0.0",
"PrimaryLUN": 0,
"PrimaryTargetIPAddress": "0.0.0.0",
"PrimaryTargetTCPPort": 3260,
"SecondaryTargetIPAddress": "127.0.0.1",
"SecondaryTargetTCPPort": 123,
"SecondaryVLANEnable": false,
"SecondaryVLANId": 1,
"TargetInfoViaDHCP": false
}
}

View File

@@ -0,0 +1,16 @@
{
"@odata.context": "/redfish/v1/$metadata#NetworkDeviceFunctionCollection.NetworkDeviceFunctionCollection",
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions",
"@odata.type": "#NetworkDeviceFunctionCollection.NetworkDeviceFunctionCollection",
"Description": "Collection Of Network Device Function entities",
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1"
}
],
"Members@odata.count": 2,
"Name": "Network Device Function Collection"
}

View File

@@ -0,0 +1,55 @@
{
"@odata.context": "/redfish/v1/$metadata#NetworkPort.NetworkPort",
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1",
"@odata.type": "#NetworkPort.v1_2_3.NetworkPort",
"ActiveLinkTechnology": "Ethernet",
"AssociatedNetworkAddresses": [
"01:02:03:04:05:06"
],
"CurrentLinkSpeedMbps": 10000,
"Description": "Network Port View",
"FlowControlConfiguration": "None",
"FlowControlStatus": "None",
"Id": "NIC.Integrated.1-1",
"LinkStatus": "Up",
"Name": "Network Port View",
"NetDevFuncMaxBWAlloc": [
{
"MaxBWAllocPercent": 100,
"NetworkDeviceFunction": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1"
}
}
],
"NetDevFuncMaxBWAlloc@odata.count": 1,
"NetDevFuncMinBWAlloc": [
{
"MinBWAllocPercent": 100,
"NetworkDeviceFunction": {
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1"
}
}
],
"NetDevFuncMinBWAlloc@odata.count": 1,
"Oem": {},
"PhysicalPortNumber": "1",
"Status": {
"State": "Enabled",
"Health": "OK",
"HealthRollup": "OK"
},
"SupportedEthernetCapabilities": [
"WakeOnLAN"
],
"SupportedEthernetCapabilities@odata.count": 1,
"SupportedLinkCapabilities": [
{
"AutoSpeedNegotiation": true,
"LinkNetworkTechnology": "Ethernet",
"LinkSpeedMbps": 10000
}
],
"SupportedLinkCapabilities@odata.count": 1,
"VendorId": "15b3",
"WakeOnLANEnabled": false
}

View File

@@ -0,0 +1,16 @@
{
"@odata.context": "/redfish/v1/$metadata#NetworkPortCollection.NetworkPortCollection",
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts",
"@odata.type": "#NetworkPortCollection.NetworkPortCollection",
"Description": "Collection Of Network Port entities",
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1"
},
{
"@odata.id": "/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2"
}
],
"Members@odata.count": 2,
"Name": "Network Port Collection"
}

View File

@@ -16,6 +16,8 @@ from unittest import mock
from sushy.resources import constants as res_cons
from sushy.resources.system.network import adapter
from sushy.resources.system.network import device_function
from sushy.resources.system.network import port
from sushy.tests.unit import base
@@ -48,6 +50,93 @@ class NetworkAdapterTestCase(base.TestCase):
self.assertEqual(res_cons.HEALTH_OK, self.adapter.status.health)
self.assertEqual(res_cons.HEALTH_OK, self.adapter.status.health_rollup)
def test_network_ports(self):
self.conn.get.return_value.json.reset_mock()
with open('sushy/tests/unit/'
'json_samples/network_port_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
actual_network_ports = self.adapter.network_ports
self.assertIsInstance(actual_network_ports,
port.NetworkPortCollection)
self.conn.get.return_value.json.assert_called_once_with()
def test_network_ports_cached(self):
self.conn.get.return_value.json.reset_mock()
with open('sushy/tests/unit/'
'json_samples/network_port_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
actual_ports = self.adapter.network_ports
self.conn.get.return_value.json.reset_mock()
self.assertIs(actual_ports,
self.adapter.network_ports)
self.conn.get.return_value.json.assert_not_called()
def test_network_ports_refresh(self):
with open('sushy/tests/unit/'
'json_samples/network_port_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
ports = self.adapter.network_ports
self.assertIsInstance(ports, port.NetworkPortCollection)
with open('sushy/tests/unit/'
'json_samples/network_adapter.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.adapter.invalidate()
self.adapter.refresh(force=False)
self.assertTrue(ports._is_stale)
with open('sushy/tests/unit/'
'json_samples/network_port_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.assertIsInstance(self.adapter.network_ports,
port.NetworkPortCollection)
def test_network_device_functions(self):
self.conn.get.return_value.json.reset_mock()
with open('sushy/tests/unit/json_samples/'
'network_device_function_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
actual_functions = self.adapter.network_device_functions
self.assertIsInstance(actual_functions,
device_function.NetworkDeviceFunctionCollection)
self.conn.get.return_value.json.assert_called_once_with()
def test_network_device_functions_cached(self):
self.conn.get.return_value.json.reset_mock()
with open('sushy/tests/unit/json_samples/'
'network_device_function_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
actual_functions = self.adapter.network_device_functions
self.conn.get.return_value.json.reset_mock()
self.assertIs(actual_functions,
self.adapter.network_device_functions)
self.conn.get.return_value.json.assert_not_called()
def test_network_device_functions_refresh(self):
with open('sushy/tests/unit/json_samples/'
'network_device_function_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
functions = self.adapter.network_device_functions
self.assertIsInstance(functions,
device_function.NetworkDeviceFunctionCollection)
with open('sushy/tests/unit/json_samples/'
'network_adapter.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.adapter.invalidate()
self.adapter.refresh(force=False)
self.assertTrue(functions._is_stale)
with open('sushy/tests/unit/json_samples/'
'network_device_function_collection.json') as f:
self.conn.get.return_value.json.return_value = json.load(f)
self.assertIsInstance(self.adapter.network_device_functions,
device_function.NetworkDeviceFunctionCollection)
class NetworkAdapterCollectionTestCase(base.TestCase):

View File

@@ -0,0 +1,164 @@
# 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 json
from unittest import mock
from sushy.resources import constants as res_cons
from sushy.resources.system.network import constants as net_cons
from sushy.resources.system.network import device_function
from sushy.tests.unit import base
class NetworkDeviceFunctionTestCase(base.TestCase):
def setUp(self):
super(NetworkDeviceFunctionTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/'
'network_device_function.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.function = device_function.NetworkDeviceFunction(
self.conn,
'/redfish/v1/Chassis/Blade1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.function._parse_attributes(self.json_doc)
self.assertEqual('1.0.2', self.function.redfish_version)
self.assertEqual('NIC.Integrated.1-2-1', self.function.identity)
self.assertEqual('NetworkDeviceFunction', self.function.name)
self.assertEqual('NetworkDeviceFunction', self.function.description)
self.assertEqual(res_cons.STATE_ENABLED, self.function.status.state)
self.assertEqual(res_cons.HEALTH_OK,
self.function.status.health)
self.assertEqual(res_cons.HEALTH_OK,
self.function.status.health_rollup)
self.assertEqual(self.function.type,
net_cons.NETWORK_DEVICE_TECHNOLOGY_ETHERNET)
self.assertEqual([
net_cons.NETWORK_DEVICE_TECHNOLOGY_DISABLED,
net_cons.NETWORK_DEVICE_TECHNOLOGY_ETHERNET,
net_cons.NETWORK_DEVICE_TECHNOLOGY_ISCSI,
], self.function.capabilities)
self.assertEqual(1,
len(self.function.fibre_channel.boot_targets))
self.assertEqual('ABCEDFG',
self.function.fibre_channel.boot_targets[0].wwpn)
self.assertEqual('1234',
self.function.fibre_channel.boot_targets[0].lun_id)
self.assertIsNone(
self.function.fibre_channel.boot_targets[0].priority
)
self.assertEqual(127, self.function.max_virtual_functions)
self.assertEqual('01:01:01:01:01:D1',
self.function.ethernet.mac_address)
self.assertEqual('01:01:01:01:01:D1',
self.function.ethernet.permanent_mac_address)
self.assertEqual(1234,
self.function.ethernet.mtu_size)
self.assertEqual(1,
self.function.ethernet.vlan.vlan_id)
self.assertTrue(self.function.ethernet.vlan.vlan_enabled)
self.assertEqual(net_cons.AUTHENTICATION_METHOD_NONE,
self.function.iscsi_boot.authentication_method)
self.assertEqual(net_cons.IP_ADDRESS_TYPE_IPV4,
self.function.iscsi_boot.ip_address_type)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.initiator_ip_address)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.initiator_default_gateway)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.initiator_netmask)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.primary_dns)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.primary_dns)
self.assertEqual(0, self.function.iscsi_boot.primary_lun)
self.assertEqual('0.0.0.0',
self.function.iscsi_boot.primary_target_ip_address)
self.assertEqual(3260,
self.function.iscsi_boot.primary_target_tcp_port)
self.assertFalse(self.function.iscsi_boot.secondary_vlan_enabled)
self.assertEqual(1,
self.function.iscsi_boot.secondary_vlan_id)
self.assertEqual(123,
self.function.iscsi_boot.secondary_target_tcp_port)
class NetworkDeviceFunctionCollectionTestCase(base.TestCase):
def setUp(self):
super(NetworkDeviceFunctionCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/'
'network_device_function_collection.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.function_col = device_function.NetworkDeviceFunctionCollection(
self.conn, '/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.function_col._parse_attributes(self.json_doc)
self.assertEqual((
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1',
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1'
),
self.function_col.members_identities)
@mock.patch.object(device_function, 'NetworkDeviceFunction', autospec=True)
def test_get_member(self, device_function_mock):
self.function_col.get_member(
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1',
)
device_function_mock.assert_called_once_with(
self.function_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1',
redfish_version=self.function_col.redfish_version,
registries=None,
root=self.function_col.root)
@mock.patch.object(device_function, 'NetworkDeviceFunction', autospec=True)
def test_get_members(self, device_function_mock):
members = self.function_col.get_members()
device_function_mock.assert_has_calls([
mock.call(
self.function_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-1-1',
redfish_version=self.function_col.redfish_version,
registries=None,
root=self.function_col.root
),
mock.call(
self.function_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkDeviceFunctions/NIC.Integrated.1-2-1',
redfish_version=self.function_col.redfish_version,
registries=None,
root=self.function_col.root
),
])
self.assertIsInstance(members, list)
self.assertEqual(2, len(members))

View File

@@ -0,0 +1,115 @@
# 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 json
from unittest import mock
from sushy.resources import constants as res_cons
from sushy.resources.system.network import constants as net_cons
from sushy.resources.system.network import port
from sushy.tests.unit import base
class NetworkPortTestCase(base.TestCase):
def setUp(self):
super(NetworkPortTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/network_port.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.port = port.NetworkPort(
self.conn, '/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'/NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.port._parse_attributes(self.json_doc)
self.assertEqual('1.0.2', self.port.redfish_version)
self.assertEqual('NIC.Integrated.1-1', self.port.identity)
self.assertEqual('Network Port View', self.port.name)
self.assertEqual('Network Port View', self.port.description)
self.assertEqual(res_cons.STATE_ENABLED, self.port.status.state)
self.assertEqual(res_cons.HEALTH_OK, self.port.status.health)
self.assertEqual(res_cons.HEALTH_OK, self.port.status.health_rollup)
self.assertEqual(['01:02:03:04:05:06'],
self.port.associated_network_addresses)
self.assertEqual(10000, self.port.current_link_speed_mbps)
self.assertEqual(net_cons.FLOW_CONTROL_NONE,
self.port.flow_control_configuration)
self.assertEqual(net_cons.FLOW_CONTROL_NONE,
self.port.flow_control_status)
self.assertEqual(net_cons.LINK_STATUS_UP, self.port.link_status)
class NetworkPortCollectionTestCase(base.TestCase):
def setUp(self):
super(NetworkPortCollectionTestCase, self).setUp()
self.conn = mock.Mock()
with open('sushy/tests/unit/json_samples/'
'network_port_collection.json') as f:
self.json_doc = json.load(f)
self.conn.get.return_value.json.return_value = self.json_doc
self.port_col = port.NetworkPortCollection(
self.conn, '/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts',
redfish_version='1.0.2')
def test__parse_attributes(self):
self.port_col._parse_attributes(self.json_doc)
self.assertEqual((
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1',
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2'
),
self.port_col.members_identities)
@mock.patch.object(port, 'NetworkPort', autospec=True)
def test_get_member(self, NetworkPort_mock):
self.port_col.get_member(
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1'
)
NetworkPort_mock.assert_called_once_with(
self.port_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1',
redfish_version=self.port_col.redfish_version, registries=None,
root=self.port_col.root)
@mock.patch.object(port, 'NetworkPort', autospec=True)
def test_get_members(self, NetworkPort_mock):
members = self.port_col.get_members()
NetworkPort_mock.assert_has_calls([
mock.call(
self.port_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-1',
redfish_version=self.port_col.redfish_version, registries=None,
root=self.port_col.root
),
mock.call(
self.port_col._conn,
'/redfish/v1/Chassis/System.Embedded.1/NetworkAdapters/'
'NIC.Integrated.1/NetworkPorts/NIC.Integrated.1-2',
redfish_version=self.port_col.redfish_version, registries=None,
root=self.port_col.root
),
])
self.assertIsInstance(members, list)
self.assertEqual(2, len(members))