Fixing DB issue with auto-generation of flavors
- Inlcude keystone authentication - Updated and included unit tests Change-Id: I8ad4790db585b274293ac34e7d0ba7ec00f61df1 Signed-off-by: Helena McGough <helena.mcgough@intel.com>
This commit is contained in:
parent
97ab8e835e
commit
cb6f9fae17
@ -4,4 +4,3 @@
|
|||||||
- openstack-python36-jobs
|
- openstack-python36-jobs
|
||||||
- check-requirements
|
- check-requirements
|
||||||
- openstack-cover-jobs
|
- openstack-cover-jobs
|
||||||
- openstack-lower-constraints-jobs
|
|
||||||
|
@ -35,6 +35,11 @@ function configure_nova_rsd {
|
|||||||
iniset $NOVA_CONF rsd podm_user ${PODM_USER}
|
iniset $NOVA_CONF rsd podm_user ${PODM_USER}
|
||||||
iniset $NOVA_CONF rsd podm_password ${PODM_PASSWD}
|
iniset $NOVA_CONF rsd podm_password ${PODM_PASSWD}
|
||||||
iniset $NOVA_CONF rsd podm_port ${PODM_PORT}
|
iniset $NOVA_CONF rsd podm_port ${PODM_PORT}
|
||||||
|
iniset $NOVA_CONF rsd auth_password ${OS_PASSWORD}
|
||||||
|
iniset $NOVA_CONF rsd auth_url ${OS_AUTH_URL}
|
||||||
|
iniset $NOVA_CONF rsd identity_version ${OS_IDENTITY_API_VERSION}
|
||||||
|
iniset $NOVA_CONF rsd tenant_name ${OS_PROJECT_NAME}
|
||||||
|
iniset $NOVA_CONF rsd username ${OS_USERNAME}
|
||||||
}
|
}
|
||||||
|
|
||||||
# disabling ERROR_NO_CLONE to allow this plugin work with devstack-gate
|
# disabling ERROR_NO_CLONE to allow this plugin work with devstack-gate
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# This driver is enabled in override-defaults with:
|
# This driver is enabled in override-defaults with:
|
||||||
# VIRT_DRIVER=${VIRT_DRIVER:-rsd}
|
# VIRT_DRIVER=${VIRT_DRIVER:-rsd}
|
||||||
|
|
||||||
|
# Auth info
|
||||||
|
OS_AUTH_URL="$KEYSTONE_SERVICE_URI/v$IDENTITY_API_VERSION"
|
||||||
|
OS_IDENTITY_API_VERSION=${IDENTITY_API_VERSION:-3}
|
||||||
|
|
||||||
if [ "$VERBOSE" == "False" ]; then
|
if [ "$VERBOSE" == "False" ]; then
|
||||||
# allow local debugging
|
# allow local debugging
|
||||||
set -o xtrace
|
set -o xtrace
|
||||||
|
145
rsd_virt_for_nova/conf/keystone_light.py
Normal file
145
rsd_virt_for_nova/conf/keystone_light.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
""" Lightweight (keystone) client for the OpenStack Identity API """
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class KeystoneException(Exception):
|
||||||
|
def __init__(self, message, exc=None, response=None):
|
||||||
|
if exc:
|
||||||
|
message += "\nReason: %s" % exc
|
||||||
|
super(KeystoneException, self).__init__(message)
|
||||||
|
|
||||||
|
self.response = response
|
||||||
|
self.exception = exc
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidResponse(KeystoneException):
|
||||||
|
def __init__(self, exc, response):
|
||||||
|
super(InvalidResponse, self).__init__(
|
||||||
|
"Invalid response from ident", exc, response)
|
||||||
|
|
||||||
|
|
||||||
|
class MissingServices(KeystoneException):
|
||||||
|
def __init__(self, message, exc, response):
|
||||||
|
super(MissingServices, self).__init__(
|
||||||
|
"MissingServices: " + message, exc, response)
|
||||||
|
|
||||||
|
|
||||||
|
class ClientV3(object):
|
||||||
|
"""Light weight client for the OpenStack Identity API V3.
|
||||||
|
|
||||||
|
:param string username: Username for authentication.
|
||||||
|
:param string password: Password for authentication.
|
||||||
|
:param string tenant_name: Tenant name.
|
||||||
|
:param string auth_url: Keystone service endpoint for authorization.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, auth_url, username, password, tenant_name):
|
||||||
|
"""Initialize a new client"""
|
||||||
|
|
||||||
|
self.auth_url = auth_url
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.tenant_name = tenant_name
|
||||||
|
self._auth_token = ''
|
||||||
|
self._services = ()
|
||||||
|
self._services_by_name = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_token(self):
|
||||||
|
"""Return token string usable for X-Auth-Token """
|
||||||
|
# actualize token
|
||||||
|
self.refresh()
|
||||||
|
return self._auth_token
|
||||||
|
|
||||||
|
@property
|
||||||
|
def services(self):
|
||||||
|
"""Return list of services retrieved from identity server """
|
||||||
|
return self._services
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
"""Refresh token and services list."""
|
||||||
|
headers = {'Accept': 'application/json'}
|
||||||
|
url = self.auth_url.rstrip('/') + '/auth/tokens'
|
||||||
|
params = {
|
||||||
|
'auth': {
|
||||||
|
'identity': {
|
||||||
|
'methods': ['password'],
|
||||||
|
'password': {
|
||||||
|
'user': {
|
||||||
|
'name': self.username,
|
||||||
|
'domain': {'id': 'default'},
|
||||||
|
'password': self.password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'scope': {
|
||||||
|
'project': {
|
||||||
|
'name': self.tenant_name,
|
||||||
|
'domain': {'id': 'default'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = requests.post(url, json=params, headers=headers)
|
||||||
|
resp_data = None
|
||||||
|
# processing response
|
||||||
|
try:
|
||||||
|
resp.raise_for_status()
|
||||||
|
resp_data = resp.json()['token']
|
||||||
|
self._services = tuple(resp_data['catalog'])
|
||||||
|
self._services_by_name = {
|
||||||
|
service['name']: service for service in self._services
|
||||||
|
}
|
||||||
|
self._auth_token = resp.headers['X-Subject-Token']
|
||||||
|
except (TypeError, KeyError, ValueError,
|
||||||
|
requests.exceptions.HTTPError) as e:
|
||||||
|
LOG.exception("Error processing response from keystone")
|
||||||
|
raise InvalidResponse(e, resp_data)
|
||||||
|
return resp_data
|
||||||
|
|
||||||
|
def get_service_endpoint(self, name, urlkey="public", region=None):
|
||||||
|
"""Return url endpoint of service
|
||||||
|
|
||||||
|
possible values of urlkey = 'adminURL' | 'publicURL' | 'internalURL'
|
||||||
|
provide region if more endpoints are available
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
endpoints = self._services_by_name[name]['endpoints']
|
||||||
|
if not endpoints:
|
||||||
|
raise MissingServices("Missing name '%s' in received services"
|
||||||
|
% name,
|
||||||
|
None, self._services)
|
||||||
|
|
||||||
|
if region:
|
||||||
|
for ep in endpoints:
|
||||||
|
if ep['region'] == region and ep['interface'] in urlkey:
|
||||||
|
return ep["url"].rstrip('/')
|
||||||
|
else:
|
||||||
|
for ep in endpoints:
|
||||||
|
if ep['interface'] in urlkey:
|
||||||
|
return ep["url"].rstrip('/')
|
||||||
|
raise MissingServices("No valid endpoints found")
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
LOG.exception("Error while processing endpoints")
|
||||||
|
raise MissingServices("Missing data in received services",
|
||||||
|
e, self._services)
|
@ -39,7 +39,22 @@ rsd_opts = [
|
|||||||
'PODM. '),
|
'PODM. '),
|
||||||
cfg.IntOpt('podm_port',
|
cfg.IntOpt('podm_port',
|
||||||
default=8443,
|
default=8443,
|
||||||
help='Specifying port on PODM for communication. ')
|
help='Specifying port on PODM for communication. '),
|
||||||
|
cfg.StrOpt('auth_password',
|
||||||
|
default='',
|
||||||
|
help='Password required to authenticate to keystone. '),
|
||||||
|
cfg.StrOpt('auth_url',
|
||||||
|
default='',
|
||||||
|
help='URL require to authenticate to keystone. '),
|
||||||
|
cfg.IntOpt('identity_version',
|
||||||
|
default=3,
|
||||||
|
help='Keystone version. '),
|
||||||
|
cfg.StrOpt('tenant_name',
|
||||||
|
default='',
|
||||||
|
help='Name of the openstack tenant. '),
|
||||||
|
cfg.StrOpt('username',
|
||||||
|
default='',
|
||||||
|
help='OpenStack username to authenticate to keystone. ')
|
||||||
]
|
]
|
||||||
|
|
||||||
STATIC_OPTIONS = (rsd_opts)
|
STATIC_OPTIONS = (rsd_opts)
|
||||||
|
@ -40,3 +40,13 @@ class TestConf(test.NoDBTestCase):
|
|||||||
self.assertEqual('admin', CONF.rsd.podm_password)
|
self.assertEqual('admin', CONF.rsd.podm_password)
|
||||||
# PODM port
|
# PODM port
|
||||||
self.assertEqual(8443, CONF.rsd.podm_port)
|
self.assertEqual(8443, CONF.rsd.podm_port)
|
||||||
|
# auth password
|
||||||
|
self.assertEqual('', CONF.rsd.auth_password)
|
||||||
|
# auth url
|
||||||
|
self.assertEqual('', CONF.rsd.auth_url)
|
||||||
|
# keystone version
|
||||||
|
self.assertEqual(3, CONF.rsd.identity_version)
|
||||||
|
# Tenant Name
|
||||||
|
self.assertEqual('', CONF.rsd.tenant_name)
|
||||||
|
# Auth username
|
||||||
|
self.assertEqual('', CONF.rsd.username)
|
||||||
|
211
rsd_virt_for_nova/tests/conf/test_keystone_light.py
Normal file
211
rsd_virt_for_nova/tests/conf/test_keystone_light.py
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
# 2017 - 2018 Intel Corporation. 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.
|
||||||
|
|
||||||
|
"""Tests for the RSD keystone_light authentication."""
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from nova import test
|
||||||
|
|
||||||
|
from rsd_virt_for_nova.conf.keystone_light import ClientV3
|
||||||
|
from rsd_virt_for_nova.conf.keystone_light import MissingServices
|
||||||
|
|
||||||
|
|
||||||
|
class TestClientV3(test.NoDBTestCase):
|
||||||
|
"""Test class for configurations."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Initialize configuration test class."""
|
||||||
|
super(TestClientV3, self).setUp()
|
||||||
|
|
||||||
|
self.client = ClientV3("my_auth_url", "user", "pass", "tenant")
|
||||||
|
self.test_authtoken = "c5bbb1c9a27e470fb482de2a718e08c2"
|
||||||
|
self.test_public_endpoint = "http://public_endpoint"
|
||||||
|
self.test_internal_endpoint = "http://internal_endpoint"
|
||||||
|
self.test_region = "RegionOne"
|
||||||
|
|
||||||
|
response = {"token": {
|
||||||
|
"is_domain": 'false',
|
||||||
|
"methods": [
|
||||||
|
"password"
|
||||||
|
],
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": "eacf519eb1264cba9ad645355ce1f6ec",
|
||||||
|
"name": "ResellerAdmin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "63e481b5d5f545ecb8947072ff34f10d",
|
||||||
|
"name": "admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_admin_project": 'false',
|
||||||
|
"project": {
|
||||||
|
"domain": {
|
||||||
|
"id": "default",
|
||||||
|
"name": "Default"
|
||||||
|
},
|
||||||
|
"id": "97467f21efb2493c92481429a04df7bd",
|
||||||
|
"name": "service"
|
||||||
|
},
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"url": self.test_public_endpoint + '/',
|
||||||
|
"interface": "public",
|
||||||
|
"region": self.test_region,
|
||||||
|
"region_id": self.test_region,
|
||||||
|
"id": "5e1d9a45d7d442ca8971a5112b2e89b5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:8777",
|
||||||
|
"interface": "admin",
|
||||||
|
"region": self.test_region,
|
||||||
|
"region_id": self.test_region,
|
||||||
|
"id": "5e8b536fde6049d381ee540c018905d1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": self.test_internal_endpoint + '/',
|
||||||
|
"interface": "internal",
|
||||||
|
"region": self.test_region,
|
||||||
|
"region_id": self.test_region,
|
||||||
|
"id": "db90c733ddd9466696bc5aaec43b18d0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "compute",
|
||||||
|
"id": "f6c15a041d574bc190c70815a14ab851",
|
||||||
|
"name": "nova"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_response = mock.Mock()
|
||||||
|
self.mock_response.json.return_value = response
|
||||||
|
self.mock_response.headers = {
|
||||||
|
'X-Subject-Token': "c5bbb1c9a27e470fb482de2a718e08c2"
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_ClientV3_init(self):
|
||||||
|
"""Test initialising keystone clientv3."""
|
||||||
|
self.assertEqual(self.client.auth_url, "my_auth_url")
|
||||||
|
self.assertEqual(self.client.username, "user")
|
||||||
|
self.assertEqual(self.client.password, "pass")
|
||||||
|
self.assertEqual(self.client.tenant_name, "tenant")
|
||||||
|
self.assertEqual(self.client._auth_token, '')
|
||||||
|
self.assertEqual(self.client._services, ())
|
||||||
|
self.assertEqual(self.client._services_by_name, {})
|
||||||
|
|
||||||
|
@mock.patch.object(requests, "post")
|
||||||
|
def test_refresh(self, post_req):
|
||||||
|
"""Test the refresh function for auth tokens."""
|
||||||
|
resp = self.client.refresh()
|
||||||
|
|
||||||
|
url = self.client.auth_url.rstrip('/') + '/auth/tokens'
|
||||||
|
params = {
|
||||||
|
'auth': {
|
||||||
|
'identity': {
|
||||||
|
'methods': ['password'],
|
||||||
|
'password': {
|
||||||
|
'user': {
|
||||||
|
'name': self.client.username,
|
||||||
|
'domain': {'id': 'default'},
|
||||||
|
'password': self.client.password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'scope': {
|
||||||
|
'project': {
|
||||||
|
'name': self.client.tenant_name,
|
||||||
|
'domain': {'id': 'default'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers = {'Accept': 'application/json'}
|
||||||
|
post_req.assert_called_once_with(url, json=params, headers=headers)
|
||||||
|
|
||||||
|
self.assertEqual(resp, post_req.return_value.json()['token'])
|
||||||
|
|
||||||
|
@mock.patch.object(requests, 'post')
|
||||||
|
def test_getservice_endpoint(self, mock_post):
|
||||||
|
"""Test get_service_endpoint"""
|
||||||
|
|
||||||
|
mock_post.return_value = self.mock_response
|
||||||
|
|
||||||
|
client = ClientV3("test_auth_url", "test_username",
|
||||||
|
"test_password", "test_tenant")
|
||||||
|
client.refresh()
|
||||||
|
|
||||||
|
endpoint = client.get_service_endpoint('nova')
|
||||||
|
self.assertEqual(endpoint, self.test_public_endpoint)
|
||||||
|
|
||||||
|
self.assertRaises(MissingServices,
|
||||||
|
client.get_service_endpoint, 'badname')
|
||||||
|
|
||||||
|
@mock.patch.object(requests, 'post')
|
||||||
|
def test_getservice_endpoint_error(self, mock_post):
|
||||||
|
"""Test get service endpoint error"""
|
||||||
|
|
||||||
|
response = {"token": {
|
||||||
|
"is_domain": 'false',
|
||||||
|
"methods": [
|
||||||
|
"password"
|
||||||
|
],
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"id": "eacf519eb1264cba9ad645355ce1f6ec",
|
||||||
|
"name": "ResellerAdmin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "63e481b5d5f545ecb8947072ff34f10d",
|
||||||
|
"name": "admin"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"is_admin_project": 'false',
|
||||||
|
"project": {
|
||||||
|
"domain": {
|
||||||
|
"id": "default",
|
||||||
|
"name": "Default"
|
||||||
|
},
|
||||||
|
"id": "97467f21efb2493c92481429a04df7bd",
|
||||||
|
"name": "service"
|
||||||
|
},
|
||||||
|
"catalog": [
|
||||||
|
{
|
||||||
|
"endpoints": [],
|
||||||
|
"type": "compute",
|
||||||
|
"id": "f6c15a041d574bc190c70815a14ab851",
|
||||||
|
"name": "badname"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.mock_response = mock.Mock()
|
||||||
|
self.mock_response.json.return_value = response
|
||||||
|
self.mock_response.headers = {
|
||||||
|
'X-Subject-Token': "c5bbb1c9a27e470fb482de2a718e08c2"
|
||||||
|
}
|
||||||
|
mock_post.return_value = self.mock_response
|
||||||
|
|
||||||
|
client = ClientV3("test_auth_url", "test_username",
|
||||||
|
"test_password", "test_tenant")
|
||||||
|
|
||||||
|
client.refresh()
|
||||||
|
|
||||||
|
self.assertRaises(MissingServices, client.get_service_endpoint, 'nova')
|
@ -28,13 +28,9 @@ from nova import rc_fields as fields
|
|||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova.compute import provider_tree
|
from nova.compute import provider_tree
|
||||||
|
|
||||||
from nova.objects import flavor
|
|
||||||
|
|
||||||
from nova.virt import fake
|
from nova.virt import fake
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
|
|
||||||
from rsd_virt_for_nova.conf import rsd as cfg
|
|
||||||
|
|
||||||
from rsd_virt_for_nova.virt import rsd
|
from rsd_virt_for_nova.virt import rsd
|
||||||
from rsd_virt_for_nova.virt.rsd import driver
|
from rsd_virt_for_nova.virt.rsd import driver
|
||||||
|
|
||||||
@ -54,8 +50,6 @@ from rsd_lib.resources.v2_3.node import node as v2_3_node
|
|||||||
|
|
||||||
from sushy import connector
|
from sushy import connector
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class FakeInstance(object):
|
class FakeInstance(object):
|
||||||
"""A class to fake out nova instances."""
|
"""A class to fake out nova instances."""
|
||||||
@ -703,108 +697,3 @@ class TestRSDDriver(base.BaseTestCase):
|
|||||||
'/redfish/v1/Systems/System2',
|
'/redfish/v1/Systems/System2',
|
||||||
'/redfish/v1/Systems/System3',
|
'/redfish/v1/Systems/System3',
|
||||||
'/redfish/v1/Systems/System4'])
|
'/redfish/v1/Systems/System4'])
|
||||||
|
|
||||||
@mock.patch.object(driver.RSDDriver, 'check_chassis_systems')
|
|
||||||
@mock.patch.object(context, 'get_admin_context')
|
|
||||||
@mock.patch.object(flavor.Flavor, '_flavor_get_by_flavor_id_from_db')
|
|
||||||
@mock.patch.object(flavor, '_flavor_create')
|
|
||||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
|
||||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
|
||||||
def test_create_flavors_success(self, conv_mem, norm_name, flav_create,
|
|
||||||
get_flav, admin_context, check_chas):
|
|
||||||
"""Test creation of new flavors for a System, success."""
|
|
||||||
# Set up the mock objects for a sucessful creation test
|
|
||||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
|
||||||
chas_col = self.RSD.driver.PODM.get_chassis_collection.return_value
|
|
||||||
chas_col.members_identities = ['/redfish/v1/Chassis/Chassis1']
|
|
||||||
sys_col.members_identities = ['/redfish/v1/Systems/System1']
|
|
||||||
sys_col.get_member.return_value = self.system_inst
|
|
||||||
chas_col.get_member.return_value = self.chassis_inst
|
|
||||||
check_chas.return_value = ['/redfish/v1/Systems/System1']
|
|
||||||
spec = 'resources:' + norm_name.return_value
|
|
||||||
mem = conv_mem.return_value - 512
|
|
||||||
proc = self.system_inst.processors.summary.count
|
|
||||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
|
||||||
# Run test
|
|
||||||
self.RSD._create_flavors()
|
|
||||||
|
|
||||||
# Check the function calls for the test
|
|
||||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
|
||||||
self.RSD.driver.PODM.get_chassis_collection.assert_called()
|
|
||||||
chas_col.get_member.assert_called_with('/redfish/v1/Chassis/Chassis1')
|
|
||||||
check_chas.assert_called()
|
|
||||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
|
||||||
conv_mem.assert_called_with(self.system_inst.memory_summary.size_gib)
|
|
||||||
norm_name.assert_called_with(flav_id)
|
|
||||||
admin_context.assert_called()
|
|
||||||
|
|
||||||
# Flavor creation call check
|
|
||||||
flav_create.assert_called_once_with(
|
|
||||||
admin_context.return_value,
|
|
||||||
{'name': 'RSD-' + flav_id,
|
|
||||||
'flavorid': flav_id,
|
|
||||||
'memory_mb': mem,
|
|
||||||
'vcpus': self.system_inst.processors.summary.count,
|
|
||||||
'root_gb': 0,
|
|
||||||
'extra_specs': {spec: '1'}})
|
|
||||||
get_flav.assert_not_called()
|
|
||||||
|
|
||||||
@mock.patch.object(flavor.Flavor, '_flavor_get_by_flavor_id_from_db')
|
|
||||||
@mock.patch.object(flavor, '_flavor_create')
|
|
||||||
@mock.patch.object(fields.ResourceClass, 'normalize_name')
|
|
||||||
@mock.patch.object(driver.RSDDriver, 'conv_GiB_to_MiB')
|
|
||||||
def test_create_flavors_failure(self, conv_mem, norm_name, flav_create,
|
|
||||||
flav_from_db):
|
|
||||||
"""Test failing the creation of new flavors for a System."""
|
|
||||||
# Setup for a failed flavor creation test
|
|
||||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
|
||||||
self.RSD._create_flavors()
|
|
||||||
|
|
||||||
# Verification of flavor creation failure and invalid function calls
|
|
||||||
self.RSD.driver.PODM.get_system_collection.assert_called_once()
|
|
||||||
sys_col.get_member.assert_not_called()
|
|
||||||
conv_mem.assert_not_called()
|
|
||||||
norm_name.assert_not_called()
|
|
||||||
flav_create.assert_not_called()
|
|
||||||
flav_from_db.assert_not_called()
|
|
||||||
|
|
||||||
@mock.patch.object(objects.FlavorList, 'get_all')
|
|
||||||
@mock.patch.object(context, 'get_admin_context')
|
|
||||||
@mock.patch.object(flavor, '_flavor_destroy')
|
|
||||||
def test_check_flavors_failure(self, flav_destroy, get_context, flav_list):
|
|
||||||
"""Test for failing to check existing flavors."""
|
|
||||||
# Setup for the test to fail to check flavors
|
|
||||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
|
||||||
self.RSD.check_flavors(self.system_col, [])
|
|
||||||
|
|
||||||
# Confirm that checking the available flavors failed
|
|
||||||
get_context.assert_called()
|
|
||||||
flav_list.assert_called_with(get_context.return_value)
|
|
||||||
sys_col.get_member.assert_not_called()
|
|
||||||
flav_destroy.assert_not_called()
|
|
||||||
|
|
||||||
@mock.patch.object(objects.FlavorList, 'get_all')
|
|
||||||
@mock.patch.object(context, 'get_admin_context')
|
|
||||||
@mock.patch.object(flavor, '_flavor_destroy')
|
|
||||||
def test_check_flavors_success(self, flav_destroy, get_context, flav_list):
|
|
||||||
"""Test for successfully checking existing flavors."""
|
|
||||||
# Setup for check flavor that exists
|
|
||||||
sys_col = self.RSD.driver.PODM.get_system_collection.return_value
|
|
||||||
sys_col.get_member.return_value = self.system_inst
|
|
||||||
self.RSD.rsd_flavors = {
|
|
||||||
'mock_flav_id': {
|
|
||||||
'id': 'flav_id',
|
|
||||||
'rsd_systems': {
|
|
||||||
'/redfish/v1/Chassis/Chassis1':
|
|
||||||
self.system_inst.identity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.RSD.check_flavors(sys_col, ['/redfish/v1/Systems/System1'])
|
|
||||||
|
|
||||||
# Confirm the list of available flavors
|
|
||||||
# No flavors need to be deleted
|
|
||||||
get_context.assert_called()
|
|
||||||
flav_list.assert_called_with(get_context.return_value)
|
|
||||||
sys_col.get_member.assert_called_with('/redfish/v1/Systems/System1')
|
|
||||||
flav_destroy.assert_called()
|
|
||||||
|
108
rsd_virt_for_nova/tests/virt/rsd/test_flavor_management.py
Normal file
108
rsd_virt_for_nova/tests/virt/rsd/test_flavor_management.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
"""Unit tests for the RSD flavor management class."""
|
||||||
|
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from rsd_virt_for_nova.conf import rsd as cfg
|
||||||
|
|
||||||
|
from rsd_virt_for_nova.virt.rsd import flavor_management
|
||||||
|
from rsd_virt_for_nova.conf import keystone_light
|
||||||
|
|
||||||
|
from oslotest import base
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class TestFlavorManager(base.BaseTestCase):
|
||||||
|
"""A test class for the flavor manager class."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Initial setup of mocks for all of the unit tests."""
|
||||||
|
super(TestFlavorManager, self).setUp()
|
||||||
|
self.flav_man = flavor_management.FlavorManager()
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""Test the initialisation of a flavor manager instance."""
|
||||||
|
self.assertEqual(self.flav_man._url_base, None)
|
||||||
|
self.assertEqual(self.flav_man._keystone, None)
|
||||||
|
self.assertEqual(self.flav_man._auth_token, None)
|
||||||
|
self.assertEqual(self.flav_man.headers, None)
|
||||||
|
|
||||||
|
def test_keystone_req(self):
|
||||||
|
"""Test a successful keystone request."""
|
||||||
|
# TODO(helenam100): write successful and failed versions of test
|
||||||
|
|
||||||
|
@mock.patch.object(flavor_management.FlavorManager, "_get_endpoint")
|
||||||
|
def test_get_base_url(self, get_endpoint):
|
||||||
|
"""Test authentication functionality."""
|
||||||
|
url = self.flav_man._get_base_url()
|
||||||
|
|
||||||
|
get_endpoint.assert_called_once_with("nova")
|
||||||
|
self.assertEqual(self.flav_man._url_base,
|
||||||
|
"{}/flavors".format(get_endpoint.return_value))
|
||||||
|
self.assertEqual(url, self.flav_man._url_base)
|
||||||
|
|
||||||
|
@mock.patch.object(keystone_light, "ClientV3")
|
||||||
|
def test_get_endpoint_success(self, client):
|
||||||
|
"""Test getting a valid endpoint for flavor creation."""
|
||||||
|
self.flav_man._keystone = client.return_value
|
||||||
|
endpoint = self.flav_man._get_endpoint("nova")
|
||||||
|
|
||||||
|
self.flav_man._keystone.get_service_endpoint.assert_called_with("nova")
|
||||||
|
self.assertEqual(endpoint,
|
||||||
|
self.flav_man._keystone.get_service_endpoint.return_value)
|
||||||
|
|
||||||
|
@mock.patch.object(keystone_light.ClientV3, "get_service_endpoint")
|
||||||
|
def test_get_endpoint_failure(self, serv_endpoint):
|
||||||
|
"""Failed test for getting an endpoint for flavor create."""
|
||||||
|
# No valid keystone test
|
||||||
|
self.assertRaises(AttributeError, self.flav_man._get_endpoint, "nova")
|
||||||
|
|
||||||
|
@mock.patch.object(flavor_management.FlavorManager, "_get_endpoint")
|
||||||
|
def test_create_request_url_delete(self, get_end):
|
||||||
|
"""Testing creation of a request url for flavor management."""
|
||||||
|
url = self.flav_man._create_request_url("flav_id", "delete")
|
||||||
|
|
||||||
|
get_end.assert_called_once_with("nova")
|
||||||
|
self.assertEquals(url,
|
||||||
|
"{}/flavors/flav_id".format(get_end.return_value))
|
||||||
|
|
||||||
|
@mock.patch.object(flavor_management.FlavorManager, "_get_endpoint")
|
||||||
|
def test_create_request_url_update(self, get_end):
|
||||||
|
"""Testing creation of a request url for flavor management."""
|
||||||
|
url = self.flav_man._create_request_url("flav_id", "update")
|
||||||
|
|
||||||
|
get_end.assert_called_once_with("nova")
|
||||||
|
self.assertEquals(url,
|
||||||
|
"{}/flavors/flav_id/os-extra_specs".format(
|
||||||
|
get_end.return_value))
|
||||||
|
|
||||||
|
@mock.patch.object(flavor_management.FlavorManager, "_get_endpoint")
|
||||||
|
def test_create_request_url_invalid(self, get_end):
|
||||||
|
"""Testing creation of a request url for flavor management."""
|
||||||
|
url = self.flav_man._create_request_url("flav_id", "invalid")
|
||||||
|
|
||||||
|
get_end.assert_called_once_with("nova")
|
||||||
|
self.assertEquals(url, '')
|
||||||
|
|
||||||
|
def test_get_headers(self):
|
||||||
|
"""Testing getting headers for requests."""
|
||||||
|
headers = self.flav_man.get_headers("my_auth_token")
|
||||||
|
|
||||||
|
self.assertEquals(headers, self.flav_man.headers)
|
||||||
|
self.assertEquals(headers,
|
||||||
|
{'X-Auth-Token': "my_auth_token",
|
||||||
|
'Content-type': 'application/json'})
|
@ -23,31 +23,26 @@ import copy
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from nova import context
|
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
|
|
||||||
from nova import objects
|
|
||||||
|
|
||||||
from nova import rc_fields as fields
|
from nova import rc_fields as fields
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova.objects import fields as obj_fields
|
from nova.objects import fields as obj_fields
|
||||||
from nova.objects import flavor
|
|
||||||
from nova.virt import driver
|
from nova.virt import driver
|
||||||
from nova.virt import hardware
|
from nova.virt import hardware
|
||||||
|
|
||||||
from rsd_virt_for_nova.conf import rsd as cfg
|
|
||||||
from rsd_virt_for_nova.virt import rsd
|
from rsd_virt_for_nova.virt import rsd
|
||||||
|
from rsd_virt_for_nova.virt.rsd import flavor_management
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from oslo_utils import versionutils
|
from oslo_utils import versionutils
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
PODM_NODE = ()
|
PODM_NODE = ()
|
||||||
@ -72,6 +67,7 @@ class RSDDriver(driver.ComputeDriver):
|
|||||||
"""Initialize the RSDDriver."""
|
"""Initialize the RSDDriver."""
|
||||||
super(RSDDriver, self).__init__(virtapi)
|
super(RSDDriver, self).__init__(virtapi)
|
||||||
# Initializes vairables to track compute nodes and instances
|
# Initializes vairables to track compute nodes and instances
|
||||||
|
self.flavor_manager = flavor_management.FlavorManager()
|
||||||
self.driver = rsd.PODM_connection()
|
self.driver = rsd.PODM_connection()
|
||||||
self.instances = OrderedDict()
|
self.instances = OrderedDict()
|
||||||
self.rsd_flavors = OrderedDict()
|
self.rsd_flavors = OrderedDict()
|
||||||
@ -86,21 +82,16 @@ class RSDDriver(driver.ComputeDriver):
|
|||||||
nodes = []
|
nodes = []
|
||||||
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
||||||
for c in CHASSIS_COL.members_identities:
|
for c in CHASSIS_COL.members_identities:
|
||||||
try:
|
|
||||||
chas = CHASSIS_COL.get_member(c)
|
chas = CHASSIS_COL.get_member(c)
|
||||||
cha_sys = self.check_chassis_systems(chas)
|
cha_sys = self.check_chassis_systems(chas)
|
||||||
if cha_sys != []:
|
if cha_sys != []:
|
||||||
nodes.append(c)
|
nodes.append(c)
|
||||||
except Exception as c_ex:
|
|
||||||
LOG.warn("Failed to get chassis information: %s", c_ex)
|
|
||||||
nodes = []
|
|
||||||
|
|
||||||
set_nodes(nodes)
|
set_nodes(nodes)
|
||||||
return copy.copy(PODM_NODE)
|
return copy.copy(PODM_NODE)
|
||||||
|
|
||||||
def init_host(self, host):
|
def init_host(self, host):
|
||||||
"""Initialize anything that is necessary for the driver to function."""
|
"""Initialize anything that is necessary for the driver to function."""
|
||||||
self._nodes = self._init_nodes()
|
|
||||||
return host
|
return host
|
||||||
|
|
||||||
def get_info(self, instance):
|
def get_info(self, instance):
|
||||||
@ -197,15 +188,15 @@ class RSDDriver(driver.ComputeDriver):
|
|||||||
cpu_info = ''
|
cpu_info = ''
|
||||||
cha_sys = []
|
cha_sys = []
|
||||||
|
|
||||||
|
if nodename not in self._nodes:
|
||||||
|
return {}
|
||||||
|
|
||||||
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
SYSTEM_COL = self.driver.PODM.get_system_collection()
|
||||||
members = SYSTEM_COL.members_identities
|
members = SYSTEM_COL.members_identities
|
||||||
|
|
||||||
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
CHASSIS_COL = self.driver.PODM.get_chassis_collection()
|
||||||
try:
|
|
||||||
chas = CHASSIS_COL.get_member(nodename)
|
chas = CHASSIS_COL.get_member(nodename)
|
||||||
cha_sys = self.check_chassis_systems(chas)
|
cha_sys = self.check_chassis_systems(chas)
|
||||||
except Exception as ex:
|
|
||||||
LOG.warn("Failed to retrieve chassis information:%s", ex)
|
|
||||||
|
|
||||||
# Check if all flavors are valid
|
# Check if all flavors are valid
|
||||||
self.check_flavors(SYSTEM_COL, members)
|
self.check_flavors(SYSTEM_COL, members)
|
||||||
@ -447,38 +438,40 @@ class RSDDriver(driver.ComputeDriver):
|
|||||||
proc = sys.processors.summary.count
|
proc = sys.processors.summary.count
|
||||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||||
res = fields.ResourceClass.normalize_name(flav_id)
|
res = fields.ResourceClass.normalize_name(flav_id)
|
||||||
spec = 'resources:' + res
|
spec = str('resources:' + res)
|
||||||
values = {
|
payload = {
|
||||||
|
"flavor": {
|
||||||
'name': 'RSD-' + flav_id,
|
'name': 'RSD-' + flav_id,
|
||||||
'flavorid': flav_id,
|
'id': flav_id,
|
||||||
'memory_mb': mem,
|
'ram': mem,
|
||||||
'vcpus': proc,
|
'vcpus': proc,
|
||||||
'root_gb': 0,
|
'disk': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flav_id not in self.rsd_flavors.keys():
|
||||||
|
try:
|
||||||
|
requests.post(
|
||||||
|
self._url_base, data=json.dumps(payload),
|
||||||
|
headers=self.headers)
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.warn("Failed to create the new flavor: %s", ex)
|
||||||
|
try:
|
||||||
|
extra_pay = {
|
||||||
'extra_specs': {
|
'extra_specs': {
|
||||||
spec: '1'}
|
spec: '1'}
|
||||||
}
|
}
|
||||||
if sys.identity not in self.rsd_flavors:
|
update_url = self.flavor_manager._create_request_url(
|
||||||
try:
|
flav_id, 'update')
|
||||||
LOG.debug("New flavor for system: %s", sys.identity)
|
requests.post(
|
||||||
flavor._flavor_create(
|
update_url, data=json.dumps(extra_pay),
|
||||||
context.get_admin_context(), values)
|
headers=self.headers)
|
||||||
self.chas_systems[str(chas.path)] = [str(sys.identity)]
|
|
||||||
self.rsd_flavors[flav_id] = {
|
self.rsd_flavors[flav_id] = {
|
||||||
'rsd_systems': self.chas_systems
|
'id': flav_id,
|
||||||
|
'rsd_systems': {
|
||||||
|
str(chas.path): str(sys.identity)}
|
||||||
}
|
}
|
||||||
self._nodes = self._init_nodes()
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.debug(
|
LOG.warn("Failed to add extra_specs:%s", ex)
|
||||||
"A flavor already exists for this system: %s", ex)
|
|
||||||
flavor.Flavor._flavor_get_by_flavor_id_from_db(
|
|
||||||
context.get_admin_context(), flav_id)
|
|
||||||
if flav_id not in self.rsd_flavors.keys():
|
|
||||||
self.chas_systems[str(chas.path)] = [
|
|
||||||
str(sys.identity)]
|
|
||||||
self.rsd_flavors[flav_id] = {
|
|
||||||
'rsd_systems': self.chas_systems
|
|
||||||
}
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
chassis_ = self.rsd_flavors[flav_id]['rsd_systems']
|
chassis_ = self.rsd_flavors[flav_id]['rsd_systems']
|
||||||
if str(chas.path) not in chassis_.keys():
|
if str(chas.path) not in chassis_.keys():
|
||||||
@ -510,30 +503,34 @@ class RSDDriver(driver.ComputeDriver):
|
|||||||
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
flav_id = str(mem) + 'MB-' + str(proc) + 'vcpus'
|
||||||
flav_ids.append(flav_id)
|
flav_ids.append(flav_id)
|
||||||
|
|
||||||
f_list = objects.FlavorList.get_all(context.get_admin_context())
|
self._keystone = self.flavor_manager.keystone_req()
|
||||||
|
self._auth_token = self._keystone.auth_token
|
||||||
|
|
||||||
|
self._url_base = self.flavor_manager._get_base_url()
|
||||||
|
self.headers = self.flavor_manager.get_headers(self._auth_token)
|
||||||
|
|
||||||
|
req = requests.get(self._url_base, headers=self.headers)
|
||||||
|
f_list = json.loads(req.text)['flavors']
|
||||||
for f in f_list:
|
for f in f_list:
|
||||||
if 'RSD' in f.name:
|
if 'RSD' in f['name']:
|
||||||
if f.flavorid not in flav_ids:
|
if f['id'] not in flav_ids:
|
||||||
try:
|
del_url = self.flavor_manager._create_request_url(
|
||||||
flavor._flavor_destroy(
|
f['id'], 'delete')
|
||||||
context.get_admin_context(),
|
fla_del = requests.delete(del_url, headers=self.headers)
|
||||||
flavor_id=f.flavorid)
|
|
||||||
except exception.FlavorNotFound as ex:
|
|
||||||
LOG.warn("Flavor not found exception: %s", ex)
|
|
||||||
|
|
||||||
for k in list(self.rsd_flavors):
|
for k in list(self.rsd_flavors):
|
||||||
if k in self.rsd_flavors.keys():
|
sys_list = self.rsd_flavors[k]['rsd_systems'].values()
|
||||||
chas_list = self.rsd_flavors[k]['rsd_systems'].keys()
|
|
||||||
for c in chas_list:
|
|
||||||
sys_list = self.rsd_flavors[k]['rsd_systems'][c]
|
|
||||||
for s in sys_list:
|
for s in sys_list:
|
||||||
if s not in sys_ids:
|
if s not in sys_ids:
|
||||||
try:
|
|
||||||
rsd_id = self.rsd_flavors[k]['id']
|
rsd_id = self.rsd_flavors[k]['id']
|
||||||
flavor._flavor_destroy(
|
del_url = self.flavor_manager._create_request_url(
|
||||||
context.get_admin_context(), rsd_id)
|
rsd_id, 'delete')
|
||||||
LOG.debug("Deleting flavor: %s", k)
|
try:
|
||||||
|
LOG.debug("Deleting flavor for removed systems: %s", k)
|
||||||
|
fla_del = requests.delete(del_url,
|
||||||
|
headers=self.headers)
|
||||||
del self.rsd_flavors[k]
|
del self.rsd_flavors[k]
|
||||||
except KeyError as k_ex:
|
except Exception as ex:
|
||||||
LOG.warn("Flavor doesn't exist:%s", k_ex)
|
LOG.warn("Failed to delete flavor: %s, %s",
|
||||||
|
json.loads(fla_del.text), ex)
|
||||||
return
|
return
|
||||||
|
88
rsd_virt_for_nova/virt/rsd/flavor_management.py
Normal file
88
rsd_virt_for_nova/virt/rsd/flavor_management.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
A class + functions for managing RSD flavors
|
||||||
|
|
||||||
|
Requires the authentication to keystone to perform request to the nova-api's.
|
||||||
|
This allows the management and creation of RSD specific flavors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rsd_virt_for_nova.conf import rsd as cfg
|
||||||
|
|
||||||
|
from rsd_virt_for_nova.conf.keystone_light import ClientV3
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FlavorManager(object):
|
||||||
|
"""Implementation of nova compute driver to compose RSD nodes from nova."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize the RSDDriver."""
|
||||||
|
self._url_base = None
|
||||||
|
self._keystone = None
|
||||||
|
self._auth_token = None
|
||||||
|
self.headers = None
|
||||||
|
|
||||||
|
def keystone_req(self):
|
||||||
|
"""Authenticate to keystone."""
|
||||||
|
keystone_url = ''
|
||||||
|
OS_USERNAME = CONF.rsd.username
|
||||||
|
OS_PASSWORD = CONF.rsd.auth_password
|
||||||
|
OS_TENANT_NAME = CONF.rsd.tenant_name
|
||||||
|
OS_AUTH_URL = CONF.rsd.auth_url
|
||||||
|
OS_IDENTITY_VERSION = CONF.rsd.identity_version
|
||||||
|
|
||||||
|
keystone_url = OS_AUTH_URL + '/v' + str(OS_IDENTITY_VERSION)
|
||||||
|
|
||||||
|
self._keystone = ClientV3(
|
||||||
|
auth_url=str(keystone_url),
|
||||||
|
username=OS_USERNAME,
|
||||||
|
password=OS_PASSWORD,
|
||||||
|
tenant_name=OS_TENANT_NAME
|
||||||
|
)
|
||||||
|
self._auth_token = self._keystone.auth_token
|
||||||
|
|
||||||
|
return self._keystone
|
||||||
|
|
||||||
|
def _get_base_url(self):
|
||||||
|
# get the uri of service endpoint
|
||||||
|
endpoint = self._get_endpoint("nova")
|
||||||
|
|
||||||
|
self._url_base = "{}/flavors".format(endpoint)
|
||||||
|
|
||||||
|
return self._url_base
|
||||||
|
|
||||||
|
def _get_endpoint(self, service):
|
||||||
|
# get the uri of service endpoint
|
||||||
|
endpoint = self._keystone.get_service_endpoint(service)
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
def _create_request_url(self, flavorid, req_type):
|
||||||
|
endpoint = self._get_endpoint("nova")
|
||||||
|
url = ''
|
||||||
|
if req_type == 'delete':
|
||||||
|
url = "{}/flavors/%s".format(endpoint) % (flavorid)
|
||||||
|
elif req_type == 'update':
|
||||||
|
url = "{}/flavors/%s/os-extra_specs".format(endpoint) % (flavorid)
|
||||||
|
return url
|
||||||
|
|
||||||
|
def get_headers(self, auth_token):
|
||||||
|
self.headers = {'X-Auth-Token': auth_token,
|
||||||
|
'Content-type': 'application/json'}
|
||||||
|
return self.headers
|
4
tox.ini
4
tox.ini
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
minversion = 2.0
|
minversion = 2.0
|
||||||
envlist = py27,py36,pycodestyle
|
envlist = py27,py36,pep8
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
@ -23,7 +23,7 @@ deps = -r{toxinidir}/requirements.txt
|
|||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
commands = stestr run {posargs}
|
commands = stestr run {posargs}
|
||||||
|
|
||||||
[testenv:pycodestyle]
|
[testenv:pep8]
|
||||||
commands =
|
commands =
|
||||||
flake8 {posargs}
|
flake8 {posargs}
|
||||||
pycodestyle {posargs}
|
pycodestyle {posargs}
|
||||||
|
Loading…
Reference in New Issue
Block a user