Delete unnecessary Manager in RSD 2.2
Change-Id: I00cb34d9f7abfac7426355e37a7cefb0a79ee378
This commit is contained in:
parent
633e41d038
commit
0f1832b67d
@ -17,7 +17,6 @@ from sushy.resources import base
|
|||||||
|
|
||||||
from rsd_lib.resources import v2_1
|
from rsd_lib.resources import v2_1
|
||||||
from rsd_lib.resources.v2_2.ethernet_switch import ethernet_switch
|
from rsd_lib.resources.v2_2.ethernet_switch import ethernet_switch
|
||||||
from rsd_lib.resources.v2_2.manager import manager
|
|
||||||
from rsd_lib.resources.v2_2.node import node
|
from rsd_lib.resources.v2_2.node import node
|
||||||
from rsd_lib.resources.v2_2.system import system
|
from rsd_lib.resources.v2_2.system import system
|
||||||
from rsd_lib.resources.v2_2.telemetry import telemetry
|
from rsd_lib.resources.v2_2.telemetry import telemetry
|
||||||
@ -27,21 +26,24 @@ from rsd_lib.resources.v2_2.update_service import update_service
|
|||||||
class RSDLibV2_2(v2_1.RSDLibV2_1):
|
class RSDLibV2_2(v2_1.RSDLibV2_1):
|
||||||
|
|
||||||
_ethernet_switches_path = base.Field(
|
_ethernet_switches_path = base.Field(
|
||||||
['Oem', 'Intel_RackScale', 'EthernetSwitches', '@odata.id'])
|
["Oem", "Intel_RackScale", "EthernetSwitches", "@odata.id"]
|
||||||
|
)
|
||||||
"""EthernetSwitchCollecton path"""
|
"""EthernetSwitchCollecton path"""
|
||||||
|
|
||||||
_nodes_path = base.Field(['Oem', 'Intel_RackScale', 'Nodes', '@odata.id'],
|
_nodes_path = base.Field(
|
||||||
required=True)
|
["Oem", "Intel_RackScale", "Nodes", "@odata.id"], required=True
|
||||||
|
)
|
||||||
"""NodeCollection path"""
|
"""NodeCollection path"""
|
||||||
|
|
||||||
_storage_service_path = base.Field(
|
_storage_service_path = base.Field(
|
||||||
['Oem', 'Intel_RackScale', 'Services', '@odata.id'])
|
["Oem", "Intel_RackScale", "Services", "@odata.id"]
|
||||||
|
)
|
||||||
"""StorageServiceCollection path"""
|
"""StorageServiceCollection path"""
|
||||||
|
|
||||||
_telemetry_service_path = base.Field(['TelemetryService', '@odata.id'])
|
_telemetry_service_path = base.Field(["TelemetryService", "@odata.id"])
|
||||||
"""Telemetry Service path"""
|
"""Telemetry Service path"""
|
||||||
|
|
||||||
_update_service_path = base.Field(['UpdateService', '@odata.id'])
|
_update_service_path = base.Field(["UpdateService", "@odata.id"])
|
||||||
"""Update Service path"""
|
"""Update Service path"""
|
||||||
|
|
||||||
def get_system(self, identity):
|
def get_system(self, identity):
|
||||||
@ -50,8 +52,9 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
:param identity: The identity of the System resource
|
:param identity: The identity of the System resource
|
||||||
:returns: The System object
|
:returns: The System object
|
||||||
"""
|
"""
|
||||||
return system.System(self._conn, identity,
|
return system.System(
|
||||||
redfish_version=self.redfish_version)
|
self._conn, identity, redfish_version=self.redfish_version
|
||||||
|
)
|
||||||
|
|
||||||
def get_system_collection(self):
|
def get_system_collection(self):
|
||||||
"""Get the SystemCollection object
|
"""Get the SystemCollection object
|
||||||
@ -60,8 +63,11 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
not found
|
not found
|
||||||
:returns: a SystemCollection object
|
:returns: a SystemCollection object
|
||||||
"""
|
"""
|
||||||
return system.SystemCollection(self._conn, self._systems_path,
|
return system.SystemCollection(
|
||||||
redfish_version=self.redfish_version)
|
self._conn,
|
||||||
|
self._systems_path,
|
||||||
|
redfish_version=self.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
def get_node_collection(self):
|
def get_node_collection(self):
|
||||||
"""Get the NodeCollection object
|
"""Get the NodeCollection object
|
||||||
@ -70,8 +76,9 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
not found
|
not found
|
||||||
:returns: a NodeCollection object
|
:returns: a NodeCollection object
|
||||||
"""
|
"""
|
||||||
return node.NodeCollection(self._conn, self._nodes_path,
|
return node.NodeCollection(
|
||||||
redfish_version=self.redfish_version)
|
self._conn, self._nodes_path, redfish_version=self.redfish_version
|
||||||
|
)
|
||||||
|
|
||||||
def get_telemetry_service(self):
|
def get_telemetry_service(self):
|
||||||
"""Given the identity return a Telemetry Service object
|
"""Given the identity return a Telemetry Service object
|
||||||
@ -79,8 +86,11 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
:param identity: The identity of the Telemetry Service resource
|
:param identity: The identity of the Telemetry Service resource
|
||||||
:returns: The Telemetry Service object
|
:returns: The Telemetry Service object
|
||||||
"""
|
"""
|
||||||
return telemetry.Telemetry(self._conn, self._telemetry_service_path,
|
return telemetry.Telemetry(
|
||||||
redfish_version=self.redfish_version)
|
self._conn,
|
||||||
|
self._telemetry_service_path,
|
||||||
|
redfish_version=self.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
def get_ethernet_switch_collection(self):
|
def get_ethernet_switch_collection(self):
|
||||||
"""Get the EthernetSwitchCollection object
|
"""Get the EthernetSwitchCollection object
|
||||||
@ -92,7 +102,7 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
return ethernet_switch.EthernetSwitchCollection(
|
return ethernet_switch.EthernetSwitchCollection(
|
||||||
self._conn,
|
self._conn,
|
||||||
self._ethernet_switches_path,
|
self._ethernet_switches_path,
|
||||||
redfish_version=self.redfish_version
|
redfish_version=self.redfish_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_ethernet_switch(self, identity):
|
def get_ethernet_switch(self, identity):
|
||||||
@ -102,30 +112,8 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
:returns: The EthernetSwitch object
|
:returns: The EthernetSwitch object
|
||||||
"""
|
"""
|
||||||
return ethernet_switch.EthernetSwitch(
|
return ethernet_switch.EthernetSwitch(
|
||||||
self._conn,
|
self._conn, identity, redfish_version=self.redfish_version
|
||||||
identity,
|
)
|
||||||
redfish_version=self.redfish_version)
|
|
||||||
|
|
||||||
def get_manager_collection(self):
|
|
||||||
"""Get the ManagerCollection object
|
|
||||||
|
|
||||||
:raises: MissingAttributeError, if the collection attribute is
|
|
||||||
not found
|
|
||||||
:returns: a ManagerCollection object
|
|
||||||
"""
|
|
||||||
return manager.ManagerCollection(self._conn,
|
|
||||||
self._managers_path,
|
|
||||||
redfish_version=self.redfish_version)
|
|
||||||
|
|
||||||
def get_manager(self, identity):
|
|
||||||
"""Given the identity return a Manager object
|
|
||||||
|
|
||||||
:param identity: The identity of the Manager resource
|
|
||||||
:returns: The Manager object
|
|
||||||
"""
|
|
||||||
return manager.Manager(self._conn,
|
|
||||||
identity,
|
|
||||||
redfish_version=self.redfish_version)
|
|
||||||
|
|
||||||
def get_update_service(self):
|
def get_update_service(self):
|
||||||
"""Get a UpdateService object
|
"""Get a UpdateService object
|
||||||
@ -133,5 +121,7 @@ class RSDLibV2_2(v2_1.RSDLibV2_1):
|
|||||||
:returns: The UpdateService object
|
:returns: The UpdateService object
|
||||||
"""
|
"""
|
||||||
return update_service.UpdateService(
|
return update_service.UpdateService(
|
||||||
self._conn, self._update_service_path,
|
self._conn,
|
||||||
redfish_version=self.redfish_version)
|
self._update_service_path,
|
||||||
|
redfish_version=self.redfish_version,
|
||||||
|
)
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
# Copyright 2019 Intel, Inc.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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 import base
|
|
||||||
|
|
||||||
from rsd_lib.resources.v2_1.manager import manager as v2_1_manager
|
|
||||||
|
|
||||||
|
|
||||||
class ManagerResetFiled(base.CompositeField):
|
|
||||||
target = base.Field("target")
|
|
||||||
"""The manager reset target"""
|
|
||||||
|
|
||||||
|
|
||||||
class ActionsField(base.CompositeField):
|
|
||||||
manager_reset = ManagerResetFiled("#Manager.Reset")
|
|
||||||
"""The actions manager reset """
|
|
||||||
|
|
||||||
|
|
||||||
class Manager(v2_1_manager.Manager):
|
|
||||||
|
|
||||||
actions = ActionsField("Actions")
|
|
||||||
"""The manager actions"""
|
|
||||||
|
|
||||||
|
|
||||||
class ManagerCollection(base.ResourceCollectionBase):
|
|
||||||
|
|
||||||
description = base.Field("Description")
|
|
||||||
"""The manager collection description"""
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _resource_type(self):
|
|
||||||
return Manager
|
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
from sushy import utils
|
from sushy import utils
|
||||||
|
|
||||||
from rsd_lib.resources.v2_2.manager import manager
|
from rsd_lib.resources.v2_1.manager import manager
|
||||||
from rsd_lib.resources.v2_3.system import ethernet_interface
|
from rsd_lib.resources.v2_3.system import ethernet_interface
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
{
|
|
||||||
"@odata.context":"/redfish/v1/$metadata#Manager.Manager",
|
|
||||||
"@odata.id":"/redfish/v1/Managers/PSME",
|
|
||||||
"@odata.type":"#Manager.v1_2_0.Manager",
|
|
||||||
"Id":"1",
|
|
||||||
"Name":"Manager",
|
|
||||||
"ManagerType":"BMC",
|
|
||||||
"Description":"BMC",
|
|
||||||
"ServiceEntryPointUUID":"92384634-2938-2342-8820-489239905423",
|
|
||||||
"UUID":"00000000-0000-0000-0000-000000000000",
|
|
||||||
"Model":"Joo Janta 200",
|
|
||||||
"DateTime":"2015-03-13T04:14:33+06:00",
|
|
||||||
"DateTimeLocalOffset":"+06:00",
|
|
||||||
"Status":{
|
|
||||||
"State":"Enabled",
|
|
||||||
"Health":"OK",
|
|
||||||
"HealthRollup":null
|
|
||||||
},
|
|
||||||
"GraphicalConsole":{
|
|
||||||
"ServiceEnabled":true,
|
|
||||||
"MaxConcurrentSessions":2,
|
|
||||||
"ConnectTypesSupported":[
|
|
||||||
"KVMIP"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"SerialConsole":{
|
|
||||||
"ServiceEnabled":true,
|
|
||||||
"MaxConcurrentSessions":1,
|
|
||||||
"ConnectTypesSupported":[
|
|
||||||
"Telnet",
|
|
||||||
"SSH",
|
|
||||||
"IPMI"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"CommandShell":{
|
|
||||||
"ServiceEnabled":true,
|
|
||||||
"MaxConcurrentSessions":4,
|
|
||||||
"ConnectTypesSupported":[
|
|
||||||
"Telnet",
|
|
||||||
"SSH"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"FirmwareVersion":"1.00",
|
|
||||||
"NetworkProtocol":{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/PSME/NetworkProtocol"
|
|
||||||
},
|
|
||||||
"EthernetInterfaces":{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/PSME/EthernetInterfaces"
|
|
||||||
},
|
|
||||||
"Links":{
|
|
||||||
"@odata.type":"#Manager.v1_1_0.Links",
|
|
||||||
"ManagerForServers":[
|
|
||||||
|
|
||||||
],
|
|
||||||
"ManagerForChassis":[
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Chassis/FabricModule1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ManagerInChassis":{
|
|
||||||
"@odata.id":"/redfish/v1/Chassis/Drawer1"
|
|
||||||
},
|
|
||||||
"Oem":{
|
|
||||||
"Intel_RackScale":{
|
|
||||||
"@odata.type":"#Intel.Oem.ManagerLinks",
|
|
||||||
"ManagerForServices":[
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Services/RSS1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ManagerForSwitches":[
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Oem":{
|
|
||||||
|
|
||||||
},
|
|
||||||
"PowerState":"On",
|
|
||||||
"Actions":{
|
|
||||||
"#Manager.Reset":{
|
|
||||||
"target":"/redfish/v1/Managers/PSME/Actions/Manager.Reset",
|
|
||||||
"ResetType@Redfish.AllowableValues":[
|
|
||||||
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Oem":{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"@odata.context":"/redfish/v1/$metadata#ManagerCollection.ManagerCollection",
|
|
||||||
"@odata.id":"/redfish/v1/Managers",
|
|
||||||
"@odata.type":"#ManagerCollection.ManagerCollection",
|
|
||||||
"Name":"Manager Collection",
|
|
||||||
"Description":"description-as-string",
|
|
||||||
"Members@odata.count":3,
|
|
||||||
"Members":[
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/BMC1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/BMC2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/PSME"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"@odata.context":"/redfish/v1/$metadata#EthernetInterface.EthernetInterface",
|
|
||||||
"@odata.id":"/redfish/v1/Systems/System1/EthernetInterfaces/LAN1",
|
|
||||||
"@odata.type":"#EthernetInterface.v1_1_0.EthernetInterface",
|
|
||||||
"Id":"LAN1",
|
|
||||||
"Name":"Ethernet Interface",
|
|
||||||
"Description":"System NIC 1",
|
|
||||||
"Status":{
|
|
||||||
"State":"Enabled",
|
|
||||||
"Health":"OK",
|
|
||||||
"HealthRollup":null
|
|
||||||
},
|
|
||||||
"InterfaceEnabled":true,
|
|
||||||
"PermanentMACAddress":"AA:BB:CC:DD:EE:FF",
|
|
||||||
"MACAddress":"AA:BB:CC:DD:EE:FF",
|
|
||||||
"SpeedMbps":100,
|
|
||||||
"AutoNeg":true,
|
|
||||||
"FullDuplex":true,
|
|
||||||
"MTUSize":1500,
|
|
||||||
"HostName":"web483",
|
|
||||||
"FQDN":"web483.redfishspecification.org",
|
|
||||||
"IPv6DefaultGateway":"fe80::3ed9:2bff:fe34:600",
|
|
||||||
"MaxIPv6StaticAddresses":null,
|
|
||||||
"NameServers":[
|
|
||||||
"names.redfishspecification.org"
|
|
||||||
],
|
|
||||||
"IPv4Addresses":[
|
|
||||||
{
|
|
||||||
"Address":"192.168.0.10",
|
|
||||||
"SubnetMask":"255.255.252.0",
|
|
||||||
"AddressOrigin":"Static",
|
|
||||||
"Gateway":"192.168.0.1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"IPv6Addresses":[
|
|
||||||
{
|
|
||||||
"Address":"fe80::1ec1:deff:fe6f:1e24",
|
|
||||||
"PrefixLength":64,
|
|
||||||
"AddressOrigin":"Static",
|
|
||||||
"AddressState":"Preferred"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"IPv6StaticAddresses":[
|
|
||||||
|
|
||||||
],
|
|
||||||
"VLAN":null,
|
|
||||||
"VLANs":null,
|
|
||||||
"Oem":{
|
|
||||||
|
|
||||||
},
|
|
||||||
"Links":{
|
|
||||||
"Oem":{
|
|
||||||
"Intel_RackScale":{
|
|
||||||
"@odata.type":"#Intel.Oem.EthernetInterface",
|
|
||||||
"NeighborPort":{
|
|
||||||
"@odata.id":"/redfish/v1/EthernetSwitches/1/Ports/1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"@odata.context":"/redfish/v1/$metadata#Managers/Members/1/EthernetInterfaces/$entity",
|
|
||||||
"@odata.id":"/redfish/v1/Managers/1/EthernetInterfaces",
|
|
||||||
"@odata.type":"#EthernetNetworkInterface.v1_0_0.EthernetNetworkInterfaceCollection",
|
|
||||||
"Name":"Ethernet Network Interface Collection",
|
|
||||||
"Description":"description-as-string",
|
|
||||||
"Members@odata.count":1,
|
|
||||||
"Members":[
|
|
||||||
{
|
|
||||||
"@odata.id":"/redfish/v1/Managers/1/EthernetInterfaces/1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
# Copyright 2019 Intel, Inc.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
from rsd_lib.resources.v2_1.system import ethernet_interface
|
|
||||||
from rsd_lib.resources.v2_2.manager import manager
|
|
||||||
|
|
||||||
from sushy.tests.unit import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestManager(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestManager, self).setUp()
|
|
||||||
self.conn = mock.Mock()
|
|
||||||
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/manager.json',
|
|
||||||
'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
|
|
||||||
self.manager_inst = manager.Manager(self.conn,
|
|
||||||
'/redfish/v1/Manager/PSME',
|
|
||||||
redfish_version='1.0.2')
|
|
||||||
|
|
||||||
def test_parse_attributes(self):
|
|
||||||
self.manager_inst._parse_attributes()
|
|
||||||
self.assertEqual('/redfish/v1/Managers/PSME/Actions/Manager.Reset',
|
|
||||||
self.manager_inst.actions.manager_reset.target)
|
|
||||||
|
|
||||||
def test_ethernet_interface(self):
|
|
||||||
# | GIVEN |
|
|
||||||
self.conn.get.return_value.json.reset_mock()
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/'
|
|
||||||
'manager_ethernet_interface.json', 'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
# | WHEN |
|
|
||||||
actual_ethernet_interfaces = self.manager_inst.ethernet_interfaces
|
|
||||||
# | THEN |
|
|
||||||
self.assertIsInstance(actual_ethernet_interfaces,
|
|
||||||
ethernet_interface.EthernetInterfaceCollection)
|
|
||||||
self.conn.get.return_value.json.assert_called_once_with()
|
|
||||||
|
|
||||||
# reset mock
|
|
||||||
self.conn.get.return_value.json.reset_mock()
|
|
||||||
# | WHEN & THEN |
|
|
||||||
# tests for same object on invoking subsequently
|
|
||||||
self.assertIs(actual_ethernet_interfaces,
|
|
||||||
self.manager_inst.ethernet_interfaces)
|
|
||||||
self.conn.get.return_value.json.assert_not_called()
|
|
||||||
|
|
||||||
def test_ethernet_interface_on_refresh(self):
|
|
||||||
# | GIVEN |
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/'
|
|
||||||
'manager_ethernet_interface.json', 'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
# | WHEN & THEN |
|
|
||||||
self.assertIsInstance(self.manager_inst.ethernet_interfaces,
|
|
||||||
ethernet_interface.EthernetInterfaceCollection)
|
|
||||||
|
|
||||||
# On refreshing the manager instance...
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/'
|
|
||||||
'manager.json', 'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
|
|
||||||
self.manager_inst.invalidate()
|
|
||||||
self.manager_inst.refresh(force=False)
|
|
||||||
|
|
||||||
# | GIVEN |
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/'
|
|
||||||
'manager_ethernet_interface.json', 'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
# | WHEN & THEN |
|
|
||||||
self.assertIsInstance(self.manager_inst.ethernet_interfaces,
|
|
||||||
ethernet_interface.EthernetInterfaceCollection)
|
|
||||||
|
|
||||||
|
|
||||||
class TestManagerCollection(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestManagerCollection, self).setUp()
|
|
||||||
self.conn = mock.Mock()
|
|
||||||
|
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/'
|
|
||||||
'manager_collection.json', 'r') as f:
|
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
|
||||||
|
|
||||||
self.manager_col = manager.ManagerCollection(self.conn,
|
|
||||||
'redfish/v1/Managers',
|
|
||||||
redfish_version='1.0.2')
|
|
||||||
|
|
||||||
def test_parse_attributes(self):
|
|
||||||
self.manager_col._parse_attributes()
|
|
||||||
self.assertEqual('description-as-string', self.manager_col.description)
|
|
||||||
self.assertEqual(('/redfish/v1/Managers/BMC1',
|
|
||||||
'/redfish/v1/Managers/BMC2',
|
|
||||||
'/redfish/v1/Managers/PSME',),
|
|
||||||
self.manager_col.members_identities)
|
|
@ -21,6 +21,7 @@ from rsd_lib.resources.v2_1.chassis import chassis as v2_1_chassis
|
|||||||
from rsd_lib.resources.v2_1.event_service import event_service \
|
from rsd_lib.resources.v2_1.event_service import event_service \
|
||||||
as v2_1_event_service
|
as v2_1_event_service
|
||||||
from rsd_lib.resources.v2_1.fabric import fabric as v2_1_fabric
|
from rsd_lib.resources.v2_1.fabric import fabric as v2_1_fabric
|
||||||
|
from rsd_lib.resources.v2_1.manager import manager as v2_1_manager
|
||||||
from rsd_lib.resources.v2_1.node import node as v2_1_node
|
from rsd_lib.resources.v2_1.node import node as v2_1_node
|
||||||
from rsd_lib.resources.v2_1.registries import message_registry_file \
|
from rsd_lib.resources.v2_1.registries import message_registry_file \
|
||||||
as v2_1_registries
|
as v2_1_registries
|
||||||
@ -30,7 +31,6 @@ from rsd_lib.resources.v2_1.task import task_service as v2_1_task_service
|
|||||||
from rsd_lib.resources import v2_2
|
from rsd_lib.resources import v2_2
|
||||||
from rsd_lib.resources.v2_2.ethernet_switch import ethernet_switch \
|
from rsd_lib.resources.v2_2.ethernet_switch import ethernet_switch \
|
||||||
as v2_2_ethernet_switch
|
as v2_2_ethernet_switch
|
||||||
from rsd_lib.resources.v2_2.manager import manager as v2_2_manager
|
|
||||||
from rsd_lib.resources.v2_2.node import node as v2_2_node
|
from rsd_lib.resources.v2_2.node import node as v2_2_node
|
||||||
from rsd_lib.resources.v2_2.system import system as v2_2_system
|
from rsd_lib.resources.v2_2.system import system as v2_2_system
|
||||||
from rsd_lib.resources.v2_2.telemetry import telemetry as v2_2_telemetry
|
from rsd_lib.resources.v2_2.telemetry import telemetry as v2_2_telemetry
|
||||||
@ -39,11 +39,10 @@ from rsd_lib.resources.v2_2.update_service import update_service \
|
|||||||
|
|
||||||
|
|
||||||
class RSDLibV2_2TestCase(testtools.TestCase):
|
class RSDLibV2_2TestCase(testtools.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(RSDLibV2_2TestCase, self).setUp()
|
super(RSDLibV2_2TestCase, self).setUp()
|
||||||
self.conn = mock.Mock()
|
self.conn = mock.Mock()
|
||||||
with open('rsd_lib/tests/unit/json_samples/v2_2/root.json', 'r') as f:
|
with open("rsd_lib/tests/unit/json_samples/v2_2/root.json", "r") as f:
|
||||||
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
self.conn.get.return_value.json.return_value = json.loads(f.read())
|
||||||
self.rsd = v2_2.RSDLibV2_2(self.conn)
|
self.rsd = v2_2.RSDLibV2_2(self.conn)
|
||||||
|
|
||||||
@ -54,170 +53,214 @@ class RSDLibV2_2TestCase(testtools.TestCase):
|
|||||||
self.assertEqual("/redfish/v1/Systems", self.rsd._systems_path)
|
self.assertEqual("/redfish/v1/Systems", self.rsd._systems_path)
|
||||||
self.assertEqual("/redfish/v1/Nodes", self.rsd._nodes_path)
|
self.assertEqual("/redfish/v1/Nodes", self.rsd._nodes_path)
|
||||||
self.assertEqual("/redfish/v1/Chassis", self.rsd._chassis_path)
|
self.assertEqual("/redfish/v1/Chassis", self.rsd._chassis_path)
|
||||||
self.assertEqual("/redfish/v1/Services",
|
self.assertEqual(
|
||||||
self.rsd._storage_service_path)
|
"/redfish/v1/Services", self.rsd._storage_service_path
|
||||||
|
)
|
||||||
self.assertEqual("/redfish/v1/Fabrics", self.rsd._fabrics_path)
|
self.assertEqual("/redfish/v1/Fabrics", self.rsd._fabrics_path)
|
||||||
self.assertEqual("/redfish/v1/Managers", self.rsd._managers_path)
|
self.assertEqual("/redfish/v1/Managers", self.rsd._managers_path)
|
||||||
self.assertEqual("/redfish/v1/TelemetryService",
|
self.assertEqual(
|
||||||
self.rsd._telemetry_service_path)
|
"/redfish/v1/TelemetryService", self.rsd._telemetry_service_path
|
||||||
self.assertEqual("/redfish/v1/EthernetSwitches",
|
)
|
||||||
self.rsd._ethernet_switches_path)
|
self.assertEqual(
|
||||||
self.assertEqual("/redfish/v1/TaskService",
|
"/redfish/v1/EthernetSwitches", self.rsd._ethernet_switches_path
|
||||||
self.rsd._task_service_path)
|
)
|
||||||
self.assertEqual("/redfish/v1/Registries",
|
self.assertEqual(
|
||||||
self.rsd._registries_path)
|
"/redfish/v1/TaskService", self.rsd._task_service_path
|
||||||
self.assertEqual("/redfish/v1/UpdateService",
|
)
|
||||||
self.rsd._update_service_path)
|
self.assertEqual("/redfish/v1/Registries", self.rsd._registries_path)
|
||||||
self.assertEqual("/redfish/v1/EventService",
|
self.assertEqual(
|
||||||
self.rsd._event_service_path)
|
"/redfish/v1/UpdateService", self.rsd._update_service_path
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
"/redfish/v1/EventService", self.rsd._event_service_path
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_system, 'SystemCollection', autospec=True)
|
@mock.patch.object(v2_2_system, "SystemCollection", autospec=True)
|
||||||
def test_get_system_collection(self, mock_system_collection):
|
def test_get_system_collection(self, mock_system_collection):
|
||||||
self.rsd.get_system_collection()
|
self.rsd.get_system_collection()
|
||||||
mock_system_collection.assert_called_once_with(
|
mock_system_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Systems',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Systems",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_system, 'System', autospec=True)
|
@mock.patch.object(v2_2_system, "System", autospec=True)
|
||||||
def test_get_system(self, mock_system):
|
def test_get_system(self, mock_system):
|
||||||
self.rsd.get_system('fake-system-id')
|
self.rsd.get_system("fake-system-id")
|
||||||
mock_system.assert_called_once_with(
|
mock_system.assert_called_once_with(
|
||||||
self.rsd._conn, 'fake-system-id',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"fake-system-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_node, 'NodeCollection', autospec=True)
|
@mock.patch.object(v2_2_node, "NodeCollection", autospec=True)
|
||||||
def test_get_node_collection(self, mock_node_collection):
|
def test_get_node_collection(self, mock_node_collection):
|
||||||
self.rsd.get_node_collection()
|
self.rsd.get_node_collection()
|
||||||
mock_node_collection.assert_called_once_with(
|
mock_node_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Nodes',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Nodes",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_node, 'Node', autospec=True)
|
@mock.patch.object(v2_1_node, "Node", autospec=True)
|
||||||
def test_get_node(self, mock_node):
|
def test_get_node(self, mock_node):
|
||||||
self.rsd.get_node('fake-node-id')
|
self.rsd.get_node("fake-node-id")
|
||||||
mock_node.assert_called_once_with(
|
mock_node.assert_called_once_with(
|
||||||
self.rsd._conn, 'fake-node-id',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"fake-node-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_fabric, 'FabricCollection', autospec=True)
|
@mock.patch.object(v2_1_fabric, "FabricCollection", autospec=True)
|
||||||
def test_get_fabric_collection(self, mock_fabric_collection):
|
def test_get_fabric_collection(self, mock_fabric_collection):
|
||||||
self.rsd.get_fabric_collection()
|
self.rsd.get_fabric_collection()
|
||||||
mock_fabric_collection.assert_called_once_with(
|
mock_fabric_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Fabrics',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Fabrics",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_fabric, 'Fabric', autospec=True)
|
@mock.patch.object(v2_1_fabric, "Fabric", autospec=True)
|
||||||
def test_get_fabric(self, mock_fabric):
|
def test_get_fabric(self, mock_fabric):
|
||||||
self.rsd.get_fabric('fake-fabric-id')
|
self.rsd.get_fabric("fake-fabric-id")
|
||||||
mock_fabric.assert_called_once_with(
|
mock_fabric.assert_called_once_with(
|
||||||
self.rsd._conn, 'fake-fabric-id',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"fake-fabric-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_chassis, 'ChassisCollection', autospec=True)
|
@mock.patch.object(v2_1_chassis, "ChassisCollection", autospec=True)
|
||||||
def test_get_chassis_collection(self, mock_chassis_collection):
|
def test_get_chassis_collection(self, mock_chassis_collection):
|
||||||
self.rsd.get_chassis_collection()
|
self.rsd.get_chassis_collection()
|
||||||
mock_chassis_collection.assert_called_once_with(
|
mock_chassis_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Chassis',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Chassis",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_chassis, 'Chassis', autospec=True)
|
@mock.patch.object(v2_1_chassis, "Chassis", autospec=True)
|
||||||
def test_get_chassis(self, mock_chassis):
|
def test_get_chassis(self, mock_chassis):
|
||||||
self.rsd.get_chassis('fake-chassis-id')
|
self.rsd.get_chassis("fake-chassis-id")
|
||||||
mock_chassis.assert_called_once_with(
|
mock_chassis.assert_called_once_with(
|
||||||
self.rsd._conn, 'fake-chassis-id',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"fake-chassis-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_storage_service, 'StorageServiceCollection',
|
@mock.patch.object(
|
||||||
autospec=True)
|
v2_1_storage_service, "StorageServiceCollection", autospec=True
|
||||||
def test_get_storage_service_collection(self,
|
)
|
||||||
mock_storage_service_collection):
|
def test_get_storage_service_collection(
|
||||||
|
self, mock_storage_service_collection
|
||||||
|
):
|
||||||
self.rsd.get_storage_service_collection()
|
self.rsd.get_storage_service_collection()
|
||||||
mock_storage_service_collection.assert_called_once_with(
|
mock_storage_service_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Services',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Services",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_storage_service, 'StorageService', autospec=True)
|
@mock.patch.object(v2_1_storage_service, "StorageService", autospec=True)
|
||||||
def test_get_storage_service(self, mock_storage_service):
|
def test_get_storage_service(self, mock_storage_service):
|
||||||
self.rsd.get_storage_service('fake-storage-service-id')
|
self.rsd.get_storage_service("fake-storage-service-id")
|
||||||
mock_storage_service.assert_called_once_with(
|
mock_storage_service.assert_called_once_with(
|
||||||
self.rsd._conn, 'fake-storage-service-id',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"fake-storage-service-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_manager, 'ManagerCollection', autospec=True)
|
@mock.patch.object(v2_1_manager, "ManagerCollection", autospec=True)
|
||||||
def test_get_manager_collection(self, mock_manager_collection):
|
def test_get_manager_collection(self, mock_manager_collection):
|
||||||
self.rsd.get_manager_collection()
|
self.rsd.get_manager_collection()
|
||||||
mock_manager_collection.assert_called_once_with(
|
mock_manager_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Managers',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Managers",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
@mock.patch.object(v2_2_manager, 'Manager', autospec=True)
|
|
||||||
def test_get_manager(self, mock_manager_service):
|
|
||||||
self.rsd.get_manager('fake-manager-id')
|
|
||||||
mock_manager_service.assert_called_once_with(
|
|
||||||
self.rsd._conn, 'fake-manager-id',
|
|
||||||
redfish_version=self.rsd.redfish_version
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_telemetry, 'Telemetry', autospec=True)
|
@mock.patch.object(v2_1_manager, "Manager", autospec=True)
|
||||||
|
def test_get_manager(self, mock_manager_service):
|
||||||
|
self.rsd.get_manager("fake-manager-id")
|
||||||
|
mock_manager_service.assert_called_once_with(
|
||||||
|
self.rsd._conn,
|
||||||
|
"fake-manager-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(v2_2_telemetry, "Telemetry", autospec=True)
|
||||||
def test_get_telemetry_service(self, mock_telemetry_service):
|
def test_get_telemetry_service(self, mock_telemetry_service):
|
||||||
self.rsd.get_telemetry_service()
|
self.rsd.get_telemetry_service()
|
||||||
mock_telemetry_service.assert_called_once_with(
|
mock_telemetry_service.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/TelemetryService',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/TelemetryService",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
@mock.patch.object(v2_2_ethernet_switch,
|
|
||||||
'EthernetSwitchCollection',
|
|
||||||
autospec=True)
|
|
||||||
def test_get_ethernet_switch_collection(self,
|
|
||||||
mock_ethernet_switch_collection):
|
|
||||||
self.rsd.get_ethernet_switch_collection()
|
|
||||||
mock_ethernet_switch_collection.assert_called_once_with(
|
|
||||||
self.rsd._conn, '/redfish/v1/EthernetSwitches',
|
|
||||||
redfish_version=self.rsd.redfish_version)
|
|
||||||
|
|
||||||
@mock.patch.object(v2_2_ethernet_switch, 'EthernetSwitch', autospec=True)
|
|
||||||
def test_get_ethernet_switch(self, mock_ethernet_switch_service):
|
|
||||||
self.rsd.get_ethernet_switch('fake-ethernet-switch-id')
|
|
||||||
mock_ethernet_switch_service.assert_called_once_with(
|
|
||||||
self.rsd._conn, 'fake-ethernet-switch-id',
|
|
||||||
redfish_version=self.rsd.redfish_version
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_task_service, 'TaskService', autospec=True)
|
@mock.patch.object(
|
||||||
def test_get_task_service(
|
v2_2_ethernet_switch, "EthernetSwitchCollection", autospec=True
|
||||||
self, mock_task_service):
|
)
|
||||||
|
def test_get_ethernet_switch_collection(
|
||||||
|
self, mock_ethernet_switch_collection
|
||||||
|
):
|
||||||
|
self.rsd.get_ethernet_switch_collection()
|
||||||
|
mock_ethernet_switch_collection.assert_called_once_with(
|
||||||
|
self.rsd._conn,
|
||||||
|
"/redfish/v1/EthernetSwitches",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(v2_2_ethernet_switch, "EthernetSwitch", autospec=True)
|
||||||
|
def test_get_ethernet_switch(self, mock_ethernet_switch_service):
|
||||||
|
self.rsd.get_ethernet_switch("fake-ethernet-switch-id")
|
||||||
|
mock_ethernet_switch_service.assert_called_once_with(
|
||||||
|
self.rsd._conn,
|
||||||
|
"fake-ethernet-switch-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(v2_1_task_service, "TaskService", autospec=True)
|
||||||
|
def test_get_task_service(self, mock_task_service):
|
||||||
self.rsd.get_task_service()
|
self.rsd.get_task_service()
|
||||||
mock_task_service.assert_called_once_with(
|
mock_task_service.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/TaskService',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/TaskService",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
v2_1_registries, 'MessageRegistryFileCollection', autospec=True)
|
v2_1_registries, "MessageRegistryFileCollection", autospec=True
|
||||||
|
)
|
||||||
def test_get_registries_collection(self, mock_registries_collection):
|
def test_get_registries_collection(self, mock_registries_collection):
|
||||||
self.rsd.get_registries_collection()
|
self.rsd.get_registries_collection()
|
||||||
mock_registries_collection.assert_called_once_with(
|
mock_registries_collection.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/Registries',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/Registries",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
@mock.patch.object(v2_1_registries, 'MessageRegistryFile', autospec=True)
|
|
||||||
def test_get_registries(self, mock_registries_service):
|
|
||||||
self.rsd.get_registries('fake-registries-id')
|
|
||||||
mock_registries_service.assert_called_once_with(
|
|
||||||
self.rsd._conn, 'fake-registries-id',
|
|
||||||
redfish_version=self.rsd.redfish_version
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_2_update_service, 'UpdateService', autospec=True)
|
@mock.patch.object(v2_1_registries, "MessageRegistryFile", autospec=True)
|
||||||
def test_get_update_service(
|
def test_get_registries(self, mock_registries_service):
|
||||||
self, mock_update_service):
|
self.rsd.get_registries("fake-registries-id")
|
||||||
|
mock_registries_service.assert_called_once_with(
|
||||||
|
self.rsd._conn,
|
||||||
|
"fake-registries-id",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(v2_2_update_service, "UpdateService", autospec=True)
|
||||||
|
def test_get_update_service(self, mock_update_service):
|
||||||
self.rsd.get_update_service()
|
self.rsd.get_update_service()
|
||||||
mock_update_service.assert_called_once_with(
|
mock_update_service.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/UpdateService',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/UpdateService",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(v2_1_event_service, 'EventService', autospec=True)
|
@mock.patch.object(v2_1_event_service, "EventService", autospec=True)
|
||||||
def test_get_event_service(self, mock_event_service):
|
def test_get_event_service(self, mock_event_service):
|
||||||
self.rsd.get_event_service()
|
self.rsd.get_event_service()
|
||||||
mock_event_service.assert_called_once_with(
|
mock_event_service.assert_called_once_with(
|
||||||
self.rsd._conn, '/redfish/v1/EventService',
|
self.rsd._conn,
|
||||||
redfish_version=self.rsd.redfish_version)
|
"/redfish/v1/EventService",
|
||||||
|
redfish_version=self.rsd.redfish_version,
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user