Show OAM Floating IP for Subcloud

Extends the dcmanager subcloud show command to display the floating
OAM IP of the subcloud from the system controller.

Change-Id: Ib421ea65660ed77a241798831c83cbaf1710f9e8
Story: 2007267
Task: 38896
Signed-off-by: Jessica Castelino <jessica.castelino@windriver.com>
This commit is contained in:
Jessica Castelino 2020-03-02 16:20:27 -05:00
parent 845d9b9af4
commit fe3ef48558
7 changed files with 212 additions and 80 deletions

View File

@ -261,7 +261,7 @@ serviceUnavailable (503)
}
******************************************************
Shows detailed information about a specific subcloud
Shows information about a specific subcloud
******************************************************
.. rest_method:: GET /v1.0/subclouds/{subcloud}
@ -350,6 +350,98 @@ internalServerError (500), serviceUnavailable (503)
This operation does not accept a request body.
******************************************************
Shows additional information about a specific subcloud
******************************************************
.. rest_method:: GET /v1.0/subclouds/<200b>{subcloud}<200b>/detail
**Normal response codes**
200
**Error response codes**
itemNotFound (404), badRequest (400), unauthorized (401), forbidden
(403), badMethod (405), HTTPUnprocessableEntity (422),
internalServerError (500), serviceUnavailable (503)
**Request parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"subcloud", "URI", "xsd:string", "The subcloud reference, name or id."
**Response parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"id (Optional)", "plain", "xsd:int", "The unique identifier for this object."
"created_at (Optional)", "plain", "xsd:dateTime", "The time when the object was created."
"updated_at (Optional)", "plain", "xsd:dateTime", "The time when the object was last updated."
"name (Optional)", "plain", "xsd:string", "The name provisioned for the subcloud."
"management (Optional)", "plain", "xsd:string", "Management state of the subcloud."
"availability (Optional)", "plain", "xsd:string", "Availability status of the subcloud."
"management-subnet (Optional)", "plain", "xsd:string", "Management subnet for subcloud in CIDR format."
"management-start-ip (Optional)", "plain", "xsd:string", "Start of management IP address range for subcloud."
"management-end-ip (Optional)", "plain", "xsd:string", "End of management IP address range for subcloud."
"systemcontroller-gateway-ip (Optional)", "plain", "xsd:string", "Systemcontroller gateway IP Address."
"endpoint_sync_status (Optional)", "plain", "xsd:list", "The list of endpoint sync statuses."
"platform_sync_status (Optional)", "plain", "xsd:string", "The platform sync status of the subcloud."
"volume_sync_status (Optional)", "plain", "xsd:string", "The volume sync status of the subcloud."
"compute_sync_status (Optional)", "plain", "xsd:string", "The compute sync status of the subcloud."
"network_sync_status (Optional)", "plain", "xsd:string", "The network sync status of the subcloud."
"patching_sync_status (Optional)", "plain", "xsd:string", "The patching sync status of the subcloud."
"oam_floating_ip (Optional)", "plain", "xsd:string", "OAM Floating IP of the subcloud."
::
{
"description": "test subcloud",
"management-start-ip": "192.168.204.50",
"created-at": "2018-02-25 19:06:35.208505",
"updated-at": "2018-02-25 21:35:59.771779",
"software-version": "18.01",
"management-state": "unmanaged",
"availability-status": "offline",
"management-subnet": "192.168.204.0/24",
"systemcontroller-gateway-ip": "192.168.204.101",
"location": "ottawa",
"endpoint_sync_status": [
{
"sync_status": "in-sync",
"endpoint_type": "compute"
},
{
"sync_status": "in-sync",
"endpoint_type": "network"
},
{
"sync_status": "in-sync",
"endpoint_type": "patching"
},
{
"sync_status": "in-sync",
"endpoint_type": "platform"
},
{
"sync_status": "in-sync",
"endpoint_type": "volume"
}
],
"management-gateway-ip": "192.168.204.1",
"management-end-ip": "192.168.204.100",
"id": 1,
"name": "subcloud6",
"oam_floating_ip" "10.10.10.12"
}
This operation does not accept a request body.
******************************
Modifies a specific subcloud
******************************
@ -449,65 +541,6 @@ Deletes a specific subcloud
This operation does not accept a request body.
****************************************************
Generates the configuration of a specific subcloud
****************************************************
.. rest_method:: POST /v1.0/subclouds/{subcloud}/config
**Normal response codes**
200
**Error response codes**
badRequest (400), unauthorized (401), forbidden (403), badMethod (405),
HTTPUnprocessableEntity (422), internalServerError (500),
serviceUnavailable (503)
**Request parameters**
.. csv-table::
:header: "Parameter", "Style", "Type", "Description"
:widths: 20, 20, 20, 60
"subcloud", "URI", "xsd:string", "The subcloud reference, name or id."
"pxe-subnet (Optional)", "plain", "xsd:string", "PXE boot boot subnet for subcloud in CIDR format."
"management-vlan (Optional)", "plain", "xsd:string", "VLAN for subcloud management network."
"management-interface-port (Optional)", "plain", "xsd:string", "Subcloud management interface port."
"management-interface-mtu (Optional)", "plain", "xsd:string", "Subcloud management interface mtu."
"oam-subnet (Optional)", "plain", "xsd:string", "OAM subnet for subcloud in CIDR format."
"oam-gateway-ip (Optional)", "plain", "xsd:string", "OAM gateway IP for subcloud."
"oam-floating-ip (Optional)", "plain", "xsd:string", "OAM floating IP address for subcloud."
"oam-unit-0-ip (Optional)", "plain", "xsd:string", "OAM unit 0 IP address for subcloud."
"oam-unit-1-ip (Optional)", "plain", "xsd:string", "OAM unit 1 IP address for subcloud."
"oam-interface-port (Optional)", "plain", "xsd:string", "Subcloud OAM interface port."
"oam-interface-mtu (Optional)", "plain", "xsd:string", "Subcloud OAM interface mtu."
"system-mode (Optional)", "plain", "xsd:string", "System mode, ``simplex, duplex, or duplex-direct``."
::
{
"oam-gateway-ip": "10.10.20.1",
"oam-interface-mtu": "1500",
"oam-subnet": "10.10.20.0/24",
"management-interface-port": "enp0s3",
"system-mode": "duplex",
"management-interface-mtu": "1500",
"oam-unit-1-ip": "10.10.20.4",
"oam-interface-port": "enp0s8",
"management-vlan": "10",
"pxe-subnet": "192.168.205.0/24",
"oam-unit-0-ip": "10.10.20.3",
"oam-floating-ip": "10.10.20.2"
}
::
{
"config": "[SYSTEM]\nSYSTEM_MODE=duplex\n[REGION2_PXEBOOT_NETWORK]\nPXEBOOT_CIDR = 192.168.205.0/24\n[MGMT_NETWORK]\nVLAN = 10\nCIDR = 192.168.204.0/24\nGATEWAY = 192.168.204.1\nIP_START_ADDRESS = 192.168.204.50\nIP_END_ADDRESS = 192.168.204.100\nDYNAMIC_ALLOCATION = Y\nLOGICAL_INTERFACE = LOGICAL_INTERFACE_1\n[LOGICAL_INTERFACE_1]\nLAG_INTERFACE = N\nINTERFACE_MTU = 1500\nINTERFACE_PORTS = enp0s3\n[OAM_NETWORK]\nCIDR = 10.10.20.0/24\nGATEWAY = 10.10.20.1\nIP_FLOATING_ADDRESS = 10.10.20.2\nIP_UNIT_0_ADDRESS = 10.10.20.3\nIP_UNIT_1_ADDRESS = 10.10.20.4\nLOGICAL_INTERFACE = LOGICAL_INTERFACE_2\n[LOGICAL_INTERFACE_2]\nLAG_INTERFACE = N\nINTERFACE_MTU = 1500\nINTERFACE_PORTS = enp0s8\n[SHARED_SERVICES]\nSYSTEM_CONTROLLER_SUBNET = 192.168.204.0/24\nSYSTEM_CONTROLLER_FLOATING_ADDRESS = 192.168.204.2\nREGION_NAME = RegionOne\nADMIN_PROJECT_NAME = admin\nADMIN_USER_NAME = admin\nADMIN_PASSWORD = Li69nux*\nKEYSTONE_ADMINURL = http://192.168.204.2:5000/v3\nKEYSTONE_SERVICE_NAME = keystone\nKEYSTONE_SERVICE_TYPE = identity\nGLANCE_SERVICE_NAME = glance\nGLANCE_SERVICE_TYPE = image\nGLANCE_CACHED = True\n[REGION_2_SERVICES]\nREGION_NAME = subcloud6\n[VERSION]\nRELEASE = 18.01\n"
}
----------------
Subcloud Alarms
----------------

View File

@ -37,6 +37,7 @@ from controllerconfig.utils import validate_address_str
from controllerconfig.utils import validate_network_str
from dcorch.drivers.openstack.keystone_v3 import KeystoneClient
from keystoneauth1 import exceptions as keystone_exceptions
from dcmanager.api.controllers import restcomm
from dcmanager.common import consts
@ -48,7 +49,6 @@ from dcmanager.db import api as db_api
from dcmanager.drivers.openstack.sysinv_v1 import SysinvClient
from dcmanager.rpc import client as rpc_client
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# System mode
@ -333,8 +333,37 @@ class SubcloudsController(object):
sysinv_client = SysinvClient(consts.DEFAULT_REGION_NAME, session)
return sysinv_client.get_management_address_pool()
@staticmethod
def get_ks_client(region_name=None):
"""This will get a new keystone client (and new token)"""
try:
return KeystoneClient(region_name)
except Exception:
LOG.warn('Failure initializing KeystoneClient '
'for region %s' % region_name)
raise
def _get_oam_addresses(self, context, subcloud_name):
"""Get the subclouds oam addresses"""
# First need to retrieve the Subcloud's Keystone session
try:
sc_ks_client = self.get_ks_client(subcloud_name)
sysinv_client = SysinvClient(subcloud_name,
sc_ks_client.session)
return sysinv_client.get_oam_addresses()
except (keystone_exceptions.EndpointNotFound, IndexError) as e:
message = ("Identity endpoint for subcloud: %s not found. %s" %
(subcloud_name, e))
LOG.error(message)
except exceptions.OAMAddressesNotFound:
message = ("OAM addresses for subcloud: %s not found." %
(subcloud_name))
LOG.error(message)
return None
@index.when(method='GET', template='json')
def get(self, subcloud_ref=None):
def get(self, subcloud_ref=None, detail=None):
"""Get details about subcloud.
:param subcloud_ref: ID or name of subcloud
@ -437,6 +466,17 @@ class SubcloudsController(object):
subcloud_status_list}
subcloud_dict.update(endpoint_sync_dict)
if detail is not None:
oam_addresses = self._get_oam_addresses(context,
subcloud_ref)
if oam_addresses is not None:
oam_floating_ip = oam_addresses.oam_floating_ip
else:
oam_floating_ip = "unavailable"
floating_ip_dict = {"oam_floating_ip":
oam_floating_ip}
subcloud_dict.update(floating_ip_dict)
return subcloud_dict
@utils.synchronized(LOCK_NAME)

View File

@ -136,6 +136,10 @@ class InternalError(DCManagerException):
message = _("Error when performing operation")
class OAMAddressesNotFound(NotFound):
message = _("OAM Addresses Not Found")
class InvalidInputError(DCManagerException):
message = _("An invalid value was provided")

View File

@ -87,18 +87,14 @@ class SysinvClient(base.DriverBase):
return self.sysinv_client.address_pool.get(address_pool_uuid)
def get_oam_address_pool(self):
def get_oam_addresses(self):
"""Get the oam address pool for a host."""
networks = self.sysinv_client.network.list()
for network in networks:
if network.type == sysinv_constants.NETWORK_TYPE_OAM:
address_pool_uuid = network.pool_uuid
break
iextoam_object = self.sysinv_client.iextoam.list()
if iextoam_object is not None and len(iextoam_object) != 0:
return iextoam_object[0]
else:
LOG.error("OAM address pool not found")
raise exceptions.InternalError()
return self.sysinv_client.address_pool.get(address_pool_uuid)
LOG.error("OAM address not found")
raise exceptions.OAMAddressesNotFound()
def create_route(self, interface_uuid, network, prefix, gateway, metric):
"""Create a static route on an interface."""

View File

@ -153,8 +153,8 @@ class SubcloudInstall(object):
raise e
def get_oam_address(self):
oam_pool = self.sysinv_client.get_oam_address_pool()
return self.format_address(oam_pool.floating_address)
oam_addresses = self.sysinv_client.get_oam_addresses()
return self.format_address(oam_addresses.oam_floating_ip)
def get_https_enabled(self):
if self.https_enabled is None:

View File

@ -487,9 +487,9 @@ class SubcloudManager(manager.Manager):
mgmt_floating_ip = mgmt_pool.floating_address
mgmt_subnet = "%s/%d" % (mgmt_pool.network, mgmt_pool.prefix)
oam_pool = sysinv_client.get_oam_address_pool()
oam_floating_ip = oam_pool.floating_address
oam_subnet = "%s/%d" % (oam_pool.network, oam_pool.prefix)
oam_addresses = sysinv_client.get_oam_addresses()
oam_floating_ip = oam_addresses.oam_floating_ip
oam_subnet = oam_addresses.oam_subnet
with open(overrides_file, 'w') as f_out_overrides_file:
f_out_overrides_file.write(

View File

@ -23,6 +23,7 @@
import copy
import mock
import six
from six.moves import http_client
import webtest
from dcmanager.api.controllers.v1 import subclouds
@ -31,7 +32,6 @@ from dcmanager.rpc import client as rpc_client
from dcmanager.tests.unit.api import test_root_controller as testroot
from dcmanager.tests import utils
FAKE_TENANT = utils.UUID1
FAKE_ID = '1'
FAKE_URL = '/v1.0/subclouds'
@ -84,6 +84,20 @@ class FakeAddressPool(object):
self.ranges.append(range)
class FakeOAMAddressPool(object):
def __init__(self, oam_subnet, oam_start_ip,
oam_end_ip, oam_c1_ip,
oam_c0_ip, oam_gateway_ip,
oam_floating_ip):
self.oam_start_ip = oam_start_ip
self.oam_end_ip = oam_end_ip
self.oam_c1_ip = oam_c1_ip
self.oam_c0_ip = oam_c0_ip
self.oam_subnet = oam_subnet
self.oam_gateway_ip = oam_gateway_ip
self.oam_floating_ip = oam_floating_ip
class TestSubclouds(testroot.DCManagerApiTest):
def setUp(self):
super(TestSubclouds, self).setUp()
@ -404,13 +418,58 @@ class TestSubclouds(testroot.DCManagerApiTest):
self.app.delete_json, delete_url,
headers=FAKE_HEADERS)
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds, 'db_api')
def test_get_subcloud(self, mock_db_api, mock_rpc_client):
def test_get_subcloud(self,
mock_db_api,
mock_rpc_client,
mock_get_oam_addresses):
get_url = FAKE_URL + '/' + FAKE_ID
self.app.get(get_url, headers=FAKE_HEADERS)
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual(response.json.get('oam_floating_ip', None), None)
self.assertEqual(1, mock_db_api.subcloud_get_with_status.call_count)
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds, 'db_api')
def test_get_subcloud_with_additional_detail(self,
mock_db_api,
mock_rpc_client,
mock_get_oam_addresses):
get_url = FAKE_URL + '/' + FAKE_ID + '/detail'
oam_addresses = FakeOAMAddressPool('10.10.10.254',
'10.10.10.1',
'10.10.10.254',
'10.10.10.4',
'10.10.10.3',
'10.10.10.1',
'10.10.10.2')
mock_get_oam_addresses.return_value = oam_addresses
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual('10.10.10.2', response.json['oam_floating_ip'])
@mock.patch.object(subclouds.SubcloudsController,
'_get_oam_addresses')
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds, 'db_api')
def test_subcloud_oam_ip_unavailable(self,
mock_db_api,
mock_rpc_client,
mock_get_oam_addresses):
get_url = FAKE_URL + '/' + FAKE_ID + '/detail'
mock_get_oam_addresses.return_value = None
response = self.app.get(get_url, headers=FAKE_HEADERS)
self.assertEqual(response.content_type, 'application/json')
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual('unavailable', response.json['oam_floating_ip'])
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds, 'db_api')
def test_get_wrong_request(self, mock_db_api, mock_rpc_client):