Introducing GEO location new fields for System

New fields was created for the system object.
Changes was made to include GEO location attributes (latitude,
longitude) to the system object and adding a way to retrieve and
modify those attributes using the API and CLI.
Updates on: DB system model; DB migration; System object fields;
API fields; CLI fields; API documentation.

Story: 2008570
Task: 41721

Signed-off-by: Daniel Pinto Barros <DanielPinto.Barros@windriver.com>
Change-Id: I86f124c44d80896427e3ac1bc799fe34588ae942
This commit is contained in:
Daniel Pinto Barros 2021-02-19 10:31:43 -05:00
parent e3a97fc267
commit 8f0312b518
8 changed files with 169 additions and 11 deletions

View File

@ -525,6 +525,8 @@ itemNotFound (404)
"timezone (Optional)", "plain", "xsd:string", "The timezone of the cloud system."
"description (Optional)", "plain", "xsd:string", "A user-specified description of the cloud system."
"location (Optional)", "plain", "xsd:string", "The user-specified location of the cloud system."
"latitude (Optional)", "plain", "xsd:string", "The user-specified latitude GPS coordinate of the cloud system."
"longitude (Optional)", "plain", "xsd:string", "The user-specified longitude GPS coordinate of the cloud system."
"capabilities (Optional)", "plain", "xsd:dictionary", "System capabilities. <ul><li>sdn_enabled : (Boolean) Software Defined Networking enabled. </li><li>region_config : (Boolean) region selection: <ul><li>true : Secondary region. </li><li>false : Primary region. </li></ul></li><li>shared_services : Services provided by Primary region. </li><li>bm_region : Board Management controller network selection: <ul><li>External : OAM network. </li><li>Internal : Management network. </li></ul></li><li>cinder_backend : backend selection for Cinder. </li><li>vswitch_type : vSwitch selection. </li><li>security_feature : Selection of Spectre and Meltdown mitigation options. </li><li>https_enabled : (Boolean) selection of https mode for public URLs. </li></ul>"
"contact (Optional)", "plain", "xsd:string", "The user-specified contact for the cloud system."
"software_version (Optional)", "plain", "xsd:string", "Contains the Cloud Server Software Version and the Software Version of the underlying Linux Kernel."
@ -555,6 +557,8 @@ itemNotFound (404)
"updated_at": "2014-09-24T14:35:38.091392+00:00",
"contact": null,
"location": null,
"latitude": null,
"longitude": null,
"description": "The Ottawa Cloud Test Lab.",
"system_type": "Standard",
"system_mode": "duplex",
@ -591,6 +595,10 @@ The attributes of the System object that are modifiable are:
- location,
- latitude,
- longitude,
- sdn_enabled,
- contact.
@ -616,6 +624,8 @@ badMediaType (415)
"timezone (Optional)", "plain", "xsd:string", "The timezone of the cloud system."
"description (Optional)", "plain", "xsd:string", "A user-specified description of the cloud system."
"location (Optional)", "plain", "xsd:string", "The user-specified location of the cloud system."
"latitude (Optional)", "plain", "xsd:string", "The user-specified latitude GPS coordinate of the cloud system."
"longitude (Optional)", "plain", "xsd:string", "The user-specified longitude GPS coordinate of the cloud system."
"capabilities (Optional)", "plain", "xsd:dictionary", "System capabilities. <ul><li>sdn_enabled : (Boolean) Software Defined Networking enabled. </li><li>region_config : (Boolean) region selection: <ul><li>true : Secondary region. </li><li>false : Primary region. </li></ul></li><li>shared_services : Services provided by Primary region. </li><li>bm_region : Board Management controller network selection: <ul><li>External : OAM network. </li><li>Internal : Management network. </li></ul></li><li>cinder_backend : backend selection for Cinder. </li><li>vswitch_type : vSwitch selection. </li><li>security_feature : Selection of Spectre and Meltdown mitigation options. </li><li>https_enabled : (Boolean) selection of https mode for public URLs. </li></ul>"
"contact (Optional)", "plain", "xsd:string", "The user-specified contact for the cloud system."
"software_version (Optional)", "plain", "xsd:string", "Contains the Cloud Server Software Version and the Software Version of the underlying Linux Kernel."
@ -642,6 +652,16 @@ badMediaType (415)
"value": "350 Terry Fox Dr, Kanata, Ontario, Canada",
"op": "replace"
}
{
"path": "/latitude",
"value": "45.35189954974955",
"op": "replace"
}
{
"path": "/longitude",
"value": "-75.91866628453701",
"op": "replace"
}
{
"path": "/contact",
"value": "support@windriver.com",
@ -684,6 +704,8 @@ badMediaType (415)
"updated_at": "2017-07-31T17:44:06.051441+00:00",
"created_at": "2017-07-31T17:35:46.836024+00:00",
"location": "350 Terry Fox Dr, Kanata, Ontario, Canada",
"latitude": "45.35189954974955",
"longitude": "-75.91866628453701",
"capabilities": {
"sdn_enabled": true,
"shared_services": "[]",

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
# Copyright (c) 2013-2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -17,9 +17,9 @@ from six.moves import input
def _print_isystem_show(isystem):
fields = ['name', 'system_type', 'system_mode', 'description', 'location',
'contact', 'timezone', 'software_version', 'uuid',
'created_at', 'updated_at', 'region_name', 'service_project_name',
'security_feature']
'latitude', 'longitude', 'contact', 'timezone', 'software_version',
'uuid', 'created_at', 'updated_at', 'region_name',
'service_project_name', 'security_feature']
if isystem.capabilities.get('region_config'):
fields.append('shared_services')
setattr(isystem, 'shared_services',
@ -76,6 +76,12 @@ def do_show(cc, args):
@utils.arg('-l', '--location',
metavar='<location>',
help='The location of the system')
@utils.arg('-la', '--latitude',
metavar='<latitude>',
help='The latitude GEO location coordinate of the system')
@utils.arg('-lo', '--longitude',
metavar='<longitude>',
help='The longitude GEO location coordinate of the system')
@utils.arg('-p', '--https_enabled',
metavar='<https_enabled>',
choices=['true', 'false', 'True', 'False'],
@ -134,8 +140,9 @@ def do_modify(cc, args):
return
print('Please follow the admin guide to complete the reconfiguration.')
field_list = ['name', 'system_mode', 'description', 'location', 'contact',
'timezone', 'sdn_enabled', 'https_enabled', 'vswitch_type', 'security_feature']
field_list = ['name', 'system_mode', 'description', 'location', 'latitude',
'longitude', 'contact', 'timezone', 'sdn_enabled',
'https_enabled', 'vswitch_type', 'security_feature']
# use field list as filter
user_fields = dict((k, v) for (k, v) in vars(args).items()

View File

@ -75,6 +75,12 @@ class System(base.APIBase):
location = wtypes.text
"The location of the isystem"
latitude = wtypes.text
"The latitude GPS coordinate of the system"
longitude = wtypes.text
"The longitude GPS coordinate of the system"
services = int
"The services of the isystem"
@ -132,8 +138,8 @@ class System(base.APIBase):
def convert_with_links(cls, rpc_isystem, expand=True):
# isystem = isystem(**rpc_isystem.as_dict())
minimum_fields = ['id', 'uuid', 'name', 'system_type', 'system_mode',
'description', 'capabilities',
'contact', 'location', 'software_version',
'description', 'capabilities', 'contact',
'location', 'latitude', 'longitude', 'software_version',
'created_at', 'updated_at', 'timezone',
'region_name', 'service_project_name',
'distributed_cloud_role', 'security_feature']
@ -409,6 +415,12 @@ class SystemController(rest.RestController):
"does not exist." %
timezone))
if (p['path'] == '/latitude' or p['path'] == '/longitude'):
if p['value'] is not None:
if len(p['value']) > 30:
raise wsme.exc.ClientSideError("Geolocation coordinates can not be "
"longer than 30 characters")
if p['path'] == '/sdn_enabled':
sdn_enabled = p['value'].lower()
patch.remove(p)
@ -557,7 +569,6 @@ class SystemController(rest.RestController):
if name or location or contact:
LOG.info("update SNMP config")
pecan.request.rpcapi.update_snmp_config(pecan.request.context)
if 'system_mode' in delta_handle:
LOG.info("update system mode %s" % system_mode)
pecan.request.rpcapi.update_system_mode_config(

View File

@ -0,0 +1,32 @@
#
# Copyright (c) 2021 Intel Corporation, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from sqlalchemy import Column
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
def upgrade(migrate_engine):
"""
This database upgrade updates the i_system table by creating
new columns to store GPS coordinates.
Arguments:
- Requires sqlalchemy migration engine
"""
meta = MetaData()
meta.bind = migrate_engine
migrate_engine.connect()
i_system = Table('i_system', meta, autoload=True)
i_system.create_column(Column('latitude', String(30)))
i_system.create_column(Column('longitude', String(30)))
def downgrade(migrate_engine):
# Downgrade is unsupported in this release.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -99,6 +99,8 @@ class isystem(Base):
capabilities = Column(JSONEncodedDict)
contact = Column(String(255))
location = Column(String(255))
latitude = Column(String(30))
longitude = Column(String(30))
services = Column(Integer, default=72)
software_version = Column(String(255))
timezone = Column(String(255))

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2016 Wind River Systems, Inc.
# Copyright (c) 2013-2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -27,6 +27,8 @@ class System(base.SysinvObject):
'capabilities': utils.dict_or_none,
'contact': utils.str_or_none,
'location': utils.str_or_none,
'latitude': utils.str_or_none,
'longitude': utils.str_or_none,
'services': utils.int_or_none,
'software_version': utils.str_or_none,
'timezone': utils.str_or_none,

View File

@ -0,0 +1,80 @@
#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
"""
Tests for the API /isystems/ methods.
"""
from sysinv.tests.api import base
from sysinv.tests.db import utils as dbutils
from six.moves import http_client
class TestSystem(base.FunctionalTest):
def setUp(self):
super(TestSystem, self).setUp()
def _get_path(self, system_id=None):
return "/isystems/%s" % system_id if system_id else "/isystems"
def _patch_and_check(self, path, updates, expect_errors=False):
patch = []
for att, val in updates.items():
patch.append({"path": "/%s" % att,
"value": val,
"op": "replace"})
# Updating system attributes
response = self.patch_json(path, patch,
expect_errors=expect_errors)
if expect_errors:
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
else:
patched_system = response.json
# Verify if system attributes was changed
for att, val in updates.items():
self.assertEqual(val, patched_system[att])
class TestSystemUpdate(TestSystem):
def setUp(self):
super(TestSystemUpdate, self).setUp()
self.system = dbutils.create_test_isystem()
def test_update_latitude_longer_than_30_chars(self):
update = {"latitude": "00.0000000111111111122222222223"}
self._patch_and_check(self._get_path(self.system.uuid),
update, expect_errors=True)
def test_update_latitude_valid_length(self):
update = {"latitude": "00.11223344556677"}
self._patch_and_check(self._get_path(self.system.uuid),
update)
def test_update_latitude_null_value(self):
update = {"latitude": None}
self._patch_and_check(self._get_path(self.system.uuid),
update)
def test_update_longitude_longer_than_30_chars(self):
update = {"longitude": "00.0000000111111111122222222223"}
self._patch_and_check(self._get_path(self.system.uuid),
update, expect_errors=True)
def test_update_longitude_valid_length(self):
update = {"longitude": "-00.11223344556677"}
self._patch_and_check(self._get_path(self.system.uuid),
update)
def test_update_longitude_null_value(self):
update = {"longitude": None}
self._patch_and_check(self._get_path(self.system.uuid),
update)

View File

@ -15,7 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2020 Wind River Systems, Inc.
# Copyright (c) 2013-2021 Wind River Systems, Inc.
#
"""Sysinv test utilities."""
@ -201,6 +201,8 @@ def get_test_isystem(**kw):
'system_mode': kw.get('system_mode', constants.SYSTEM_MODE_DUPLEX),
'region_name': kw.get('region_name', constants.REGION_ONE_NAME),
'location': kw.get('location', 'isystemlocation'),
'latitude': kw.get('latitude'),
'longitude': kw.get('longitude'),
'services': kw.get('services', 72),
'software_version': kw.get('software_version', SW_VERSION)
}