Add magnum bay api tempest tests
This will add magnum bay CRUD tempest tests The timeout to create bays is set to 10 minutes, which may need to be tweaked based on how long it takes to spin up a bay. Implements: blueprint magnum-tempest Change-Id: If4a34d7fd317e10b55d5f07bd51ffe37e6d4bc20
This commit is contained in:
parent
fbb0151fd5
commit
a66a532c99
|
@ -0,0 +1,143 @@
|
|||
# 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 tempest_lib import exceptions
|
||||
|
||||
from magnum.tests.functional.api.v1.models import bay_model
|
||||
from magnum.tests.functional.common import client
|
||||
from magnum.tests.functional.common import utils
|
||||
|
||||
|
||||
class BayClient(client.MagnumClient):
|
||||
"""Encapsulates REST calls and maps JSON to/from models"""
|
||||
|
||||
@classmethod
|
||||
def bays_uri(cls, filters=None):
|
||||
"""Construct bays uri with optional filters
|
||||
|
||||
:param filters: Optional k:v dict that's converted to url query
|
||||
:returns: url string
|
||||
"""
|
||||
|
||||
url = "/bays"
|
||||
if filters:
|
||||
url = cls.add_filters(url, filters)
|
||||
return url
|
||||
|
||||
@classmethod
|
||||
def bay_uri(cls, bay_id):
|
||||
"""Construct bay uri
|
||||
|
||||
:param bay_id: bay uuid or name
|
||||
:returns: url string
|
||||
"""
|
||||
|
||||
return "{0}/{1}".format(cls.bays_uri(), bay_id)
|
||||
|
||||
def list_bays(self, filters=None, **kwargs):
|
||||
"""Makes GET /bays request and returns BayCollection
|
||||
|
||||
Abstracts REST call to return all bays
|
||||
|
||||
:param filters: Optional k:v dict that's converted to url query
|
||||
:returns: response object and BayCollection object
|
||||
"""
|
||||
|
||||
resp, body = self.get(self.bays_uri(filters), **kwargs)
|
||||
return self.deserialize(resp, body, bay_model.BayCollection)
|
||||
|
||||
def get_bay(self, bay_id, **kwargs):
|
||||
"""Makes GET /bay request and returns BayEntity
|
||||
|
||||
Abstracts REST call to return a single bay based on uuid or name
|
||||
|
||||
:param bay_id: bay uuid or name
|
||||
:returns: response object and BayCollection object
|
||||
"""
|
||||
|
||||
resp, body = self.get(self.bay_uri(bay_id))
|
||||
return self.deserialize(resp, body, bay_model.BayEntity)
|
||||
|
||||
def post_bay(self, model, **kwargs):
|
||||
"""Makes POST /bay request and returns BayEntity
|
||||
|
||||
Abstracts REST call to create new bay
|
||||
|
||||
:param model: BayEntity
|
||||
:returns: response object and BayEntity object
|
||||
"""
|
||||
|
||||
resp, body = self.post(
|
||||
self.bays_uri(),
|
||||
body=model.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, bay_model.BayEntity)
|
||||
|
||||
def patch_bay(self, bay_id, baypatch_listmodel, **kwargs):
|
||||
"""Makes PATCH /bay request and returns BayEntity
|
||||
|
||||
Abstracts REST call to update bay attributes
|
||||
|
||||
:param bay_id: UUID of bay
|
||||
:param baypatch_listmodel: BayPatchCollection
|
||||
:returns: response object and BayEntity object
|
||||
"""
|
||||
|
||||
resp, body = self.patch(
|
||||
self.bay_uri(bay_id),
|
||||
body=baypatch_listmodel.to_json(), **kwargs)
|
||||
return self.deserialize(resp, body, bay_model.BayEntity)
|
||||
|
||||
def delete_bay(self, bay_id, **kwargs):
|
||||
"""Makes DELETE /bay request and returns response object
|
||||
|
||||
Abstracts REST call to delete bay based on uuid or name
|
||||
|
||||
:param bay_id: UUID or name of bay
|
||||
:returns: response object
|
||||
"""
|
||||
|
||||
return self.delete(self.bay_uri(bay_id), **kwargs)
|
||||
|
||||
def wait_for_bay_to_delete(self, bay_id):
|
||||
utils.wait_for_condition(
|
||||
lambda: self.does_bay_not_exist(bay_id), 10, 3600)
|
||||
|
||||
def wait_for_created_bay(self, bay_id):
|
||||
try:
|
||||
utils.wait_for_condition(
|
||||
lambda: self.does_bay_exist(bay_id), 10, 3600)
|
||||
except Exception:
|
||||
# In error state. Clean up the bay id
|
||||
self.delete_bay(bay_id)
|
||||
self.wait_for_bay_to_delete(bay_id)
|
||||
raise
|
||||
|
||||
def does_bay_exist(self, bay_id):
|
||||
try:
|
||||
resp, model = self.get_bay(bay_id)
|
||||
if model.status in ['CREATED', 'CREATE_COMPLETE']:
|
||||
return True
|
||||
elif model.status in ['ERROR', 'CREATE_FAILED']:
|
||||
raise exceptions.ServerFault(
|
||||
"Got into an error condition: %s for %s" %
|
||||
(model.status, bay_id))
|
||||
else:
|
||||
return False
|
||||
except exceptions.NotFound:
|
||||
return False
|
||||
|
||||
def does_bay_not_exist(self, bay_id):
|
||||
try:
|
||||
self.get_bay(bay_id)
|
||||
except exceptions.NotFound:
|
||||
return True
|
||||
return False
|
|
@ -0,0 +1,30 @@
|
|||
# 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 magnum.tests.functional.common import models
|
||||
|
||||
|
||||
class BayData(models.BaseModel):
|
||||
"""Data that encapsulates bay attributes"""
|
||||
pass
|
||||
|
||||
|
||||
class BayEntity(models.EntityModel):
|
||||
"""Entity Model that represents a single instance of BayData"""
|
||||
ENTITY_NAME = 'bay'
|
||||
MODEL_TYPE = BayData
|
||||
|
||||
|
||||
class BayCollection(models.CollectionModel):
|
||||
"""Collection Model that represents a list of BayData objects"""
|
||||
COLLECTION_NAME = 'baylists'
|
||||
MODEL_TYPE = BayData
|
|
@ -0,0 +1,76 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
|
||||
from magnum.tests.functional.common import models
|
||||
|
||||
|
||||
class BayPatchData(models.BaseModel):
|
||||
"""Data that encapsulates baypatch attributes"""
|
||||
pass
|
||||
|
||||
|
||||
class BayPatchEntity(models.EntityModel):
|
||||
"""Entity Model that represents a single instance of BayPatchData"""
|
||||
ENTITY_NAME = 'baypatch'
|
||||
MODEL_TYPE = BayPatchData
|
||||
|
||||
|
||||
class BayPatchCollection(models.CollectionModel):
|
||||
"""Collection Model that represents a list of BayPatchData objects"""
|
||||
MODEL_TYPE = BayPatchData
|
||||
COLLECTION_NAME = 'baypatchlist'
|
||||
|
||||
def to_json(self):
|
||||
"""Converts BayPatchCollection to json
|
||||
|
||||
Retrieves list from COLLECTION_NAME attribute and converts each object
|
||||
to dict, appending it to a list. Then converts the entire list to json
|
||||
|
||||
This is required due to COLLECTION_NAME holding a list of objects that
|
||||
needed to be converted to dict individually
|
||||
|
||||
:returns: json object
|
||||
"""
|
||||
|
||||
data = getattr(self, BayPatchCollection.COLLECTION_NAME)
|
||||
collection = []
|
||||
for d in data:
|
||||
collection.append(d.to_dict())
|
||||
return json.dumps(collection)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
"""Converts dict to BayPatchData
|
||||
|
||||
Converts data dict to list of BayPatchData objects and stores it
|
||||
in COLLECTION_NAME
|
||||
|
||||
Example of dict data:
|
||||
|
||||
[{
|
||||
"path": "/name",
|
||||
"value": "myname",
|
||||
"op": "replace"
|
||||
}]
|
||||
|
||||
:param data: dict of patch data
|
||||
:returns: json object
|
||||
"""
|
||||
|
||||
model = cls()
|
||||
collection = []
|
||||
for d in data:
|
||||
collection.append(cls.MODEL_TYPE.from_dict(d))
|
||||
setattr(model, cls.COLLECTION_NAME, collection)
|
||||
return model
|
|
@ -0,0 +1,171 @@
|
|||
# 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 fixtures
|
||||
|
||||
from oslo_log import log as logging
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions
|
||||
import testtools
|
||||
|
||||
from magnum.tests.functional.common import base
|
||||
from magnum.tests.functional.common import datagen
|
||||
|
||||
|
||||
class BayTest(base.BaseMagnumTest):
|
||||
|
||||
"""Tests for bay CRUD."""
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BayTest, self).__init__(*args, **kwargs)
|
||||
self.bays = []
|
||||
self.credentials = None
|
||||
self.baymodel = None
|
||||
self.baymodel_client = None
|
||||
self.keypairs_client = None
|
||||
self.bay_client = None
|
||||
|
||||
def setUp(self):
|
||||
super(BayTest, self).setUp()
|
||||
self.credentials = self.get_credentials(type_of_creds='default')
|
||||
(self.baymodel_client,
|
||||
self.keypairs_client) = self.get_clients_with_existing_creds(
|
||||
creds=self.credentials,
|
||||
type_of_creds='default',
|
||||
request_type='baymodel')
|
||||
(self.bay_client, _) = self.get_clients_with_existing_creds(
|
||||
creds=self.credentials,
|
||||
type_of_creds='default',
|
||||
request_type='bay')
|
||||
model = datagen.valid_swarm_baymodel()
|
||||
_, self.baymodel = self._create_baymodel(model)
|
||||
|
||||
# NOTE (dimtruck) by default tempest sets timeout to 20 mins.
|
||||
# We need more time.
|
||||
test_timeout = 3600
|
||||
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
|
||||
|
||||
def tearDown(self):
|
||||
bay_list = self.bays[:]
|
||||
for bay_id in bay_list:
|
||||
self._delete_bay(bay_id)
|
||||
self.bays.remove(bay_id)
|
||||
self._delete_baymodel(self.baymodel.uuid)
|
||||
super(BayTest, self).tearDown()
|
||||
|
||||
def _create_baymodel(self, baymodel_model):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
resp, model = self.baymodel_client.post_baymodel(baymodel_model)
|
||||
return resp, model
|
||||
|
||||
def _delete_baymodel(self, baymodel_id):
|
||||
resp, model = self.baymodel_client.delete_baymodel(baymodel_id)
|
||||
return resp, model
|
||||
|
||||
def _create_bay(self, bay_model):
|
||||
resp, model = self.bay_client.post_bay(bay_model)
|
||||
self.LOG.info('Response: %s' % resp)
|
||||
self.LOG.info('Model: %s ' % model)
|
||||
self.assertEqual(resp.status, 201)
|
||||
self.assertIsNotNone(model.uuid)
|
||||
self.assertIsNone(model.status)
|
||||
self.assertIsNone(model.status_reason)
|
||||
self.assertEqual(model.baymodel_id, self.baymodel.uuid)
|
||||
self.bay_client.wait_for_created_bay(model.uuid)
|
||||
self.bays.append(model.uuid)
|
||||
return resp, model
|
||||
|
||||
def _delete_bay(self, bay_id):
|
||||
resp, model = self.bay_client.delete_bay(bay_id)
|
||||
self.assertEqual(resp.status, 204)
|
||||
self.bay_client.wait_for_bay_to_delete(bay_id)
|
||||
return resp, model
|
||||
|
||||
def _get_bay_by_id(self, bay_id):
|
||||
resp, model = self.bay_client.get_bay(bay_id)
|
||||
self.assertEqual(resp.status, 404)
|
||||
return resp, model
|
||||
|
||||
# (dimtruck) Combining all these tests in one because
|
||||
# they time out on the gate (2 hours not enough)
|
||||
@testtools.testcase.attr('positive')
|
||||
def test_create_list_and_delete_bays(self):
|
||||
gen_model = datagen.valid_bay_data(baymodel_id=self.baymodel.uuid)
|
||||
_, temp_model = self._create_bay(gen_model)
|
||||
resp, model = self.bay_client.list_bays()
|
||||
self.assertEqual(resp.status, 200)
|
||||
self.assertGreater(len(model.bays), 0)
|
||||
self.assertIn(
|
||||
temp_model.uuid, list([x['uuid'] for x in model.bays]))
|
||||
self._delete_bay(temp_model.uuid)
|
||||
self.bays.remove(temp_model.uuid)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_bay_for_nonexisting_baymodel(self):
|
||||
gen_model = datagen.valid_bay_data(baymodel_id='this-does-not-exist')
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.bay_client.post_bay, gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_bay_with_node_count_0(self):
|
||||
gen_model = datagen.valid_bay_data(
|
||||
baymodel_id=self.baymodel.uuid, node_count=0)
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.bay_client.post_bay, gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_bay_with_zero_masters(self):
|
||||
gen_model = datagen.valid_bay_data(baymodel_id=self.baymodel.uuid,
|
||||
master_count=0)
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.bay_client.post_bay, gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_bay_with_missing_name(self):
|
||||
self.skipTest('This is currently an error! '
|
||||
'Should throw a 400 instead of a 500')
|
||||
gen_model = datagen.valid_bay_data(baymodel_id=self.baymodel.uuid,
|
||||
name=None)
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.bay_client.post_bay, gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_update_bay_name_for_existing_bay(self):
|
||||
first_model = datagen.valid_bay_data(baymodel_id=self.baymodel.uuid,
|
||||
name='test')
|
||||
_, old_model = self._create_bay(first_model)
|
||||
|
||||
patch_model = datagen.bay_name_patch_data()
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.bay_client.patch_bay,
|
||||
old_model.uuid, patch_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_update_bay_for_nonexisting_bay(self):
|
||||
patch_model = datagen.bay_name_patch_data()
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.bay_client.patch_bay, 'fooo', patch_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_delete_bay_for_nonexisting_bay(self):
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.bay_client.delete_bay, data_utils.rand_uuid())
|
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions
|
||||
import testtools
|
||||
|
||||
|
@ -31,7 +32,7 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
def setUp(self):
|
||||
super(BayModelTest, self).setUp()
|
||||
(self.baymodel_client,
|
||||
self.keypairs_client) = self.get_clients_with_isolated_creds(
|
||||
self.keypairs_client) = self.get_clients_with_new_creds(
|
||||
type_of_creds='default',
|
||||
request_type='baymodel')
|
||||
|
||||
|
@ -55,7 +56,7 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('positive')
|
||||
def test_list_baymodels(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
_, temp_model = self._create_baymodel(gen_model)
|
||||
resp, model = self.baymodel_client.list_baymodels()
|
||||
self.assertEqual(200, resp.status)
|
||||
|
@ -66,16 +67,16 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('positive')
|
||||
def test_create_baymodel(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
resp, model = self._create_baymodel(gen_model)
|
||||
|
||||
@testtools.testcase.attr('positive')
|
||||
def test_update_baymodel_by_uuid(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
resp, old_model = self._create_baymodel(gen_model)
|
||||
|
||||
patch_model = datagen.random_baymodel_name_patch_data()
|
||||
patch_model = datagen.baymodel_name_patch_data()
|
||||
resp, new_model = self.baymodel_client.patch_baymodel(
|
||||
old_model.uuid, patch_model)
|
||||
self.assertEqual(200, resp.status)
|
||||
|
@ -88,7 +89,7 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('positive')
|
||||
def test_delete_baymodel_by_uuid(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
resp, model = self._create_baymodel(gen_model)
|
||||
resp, _ = self.baymodel_client.delete_baymodel(model.uuid)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
@ -97,7 +98,7 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('positive')
|
||||
def test_delete_baymodel_by_name(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
resp, model = self._create_baymodel(gen_model)
|
||||
resp, _ = self.baymodel_client.delete_baymodel(model.name)
|
||||
self.assertEqual(204, resp.status)
|
||||
|
@ -107,22 +108,22 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
def test_get_baymodel_by_uuid_404(self):
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.baymodel_client.get_baymodel, datagen.random_uuid())
|
||||
self.baymodel_client.get_baymodel, data_utils.rand_uuid())
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_update_baymodel_404(self):
|
||||
patch_model = datagen.random_baymodel_name_patch_data()
|
||||
patch_model = datagen.baymodel_name_patch_data()
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.baymodel_client.patch_baymodel,
|
||||
datagen.random_uuid(), patch_model)
|
||||
data_utils.rand_uuid(), patch_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_delete_baymodel_404(self):
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.baymodel_client.delete_baymodel, datagen.random_uuid())
|
||||
self.baymodel_client.delete_baymodel, data_utils.rand_uuid())
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_get_baymodel_by_name_404(self):
|
||||
|
@ -132,7 +133,7 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_update_baymodel_name_not_found(self):
|
||||
patch_model = datagen.random_baymodel_name_patch_data()
|
||||
patch_model = datagen.baymodel_name_patch_data()
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
|
@ -147,14 +148,14 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('negative')
|
||||
def test_create_baymodel_missing_image(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair()
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.baymodel_client.post_baymodel, gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_baymodel_missing_keypair(self):
|
||||
gen_model = datagen.random_baymodel_data_w_valid_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_image_id()
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.baymodel_client.post_baymodel, gen_model)
|
||||
|
@ -163,18 +164,18 @@ class BayModelTest(base.BaseMagnumTest):
|
|||
def test_update_baymodel_invalid_patch(self):
|
||||
# get json object
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
resp, old_model = self._create_baymodel(gen_model)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
self.baymodel_client.patch_baymodel, datagen.random_uuid(),
|
||||
self.baymodel_client.patch_baymodel, data_utils.rand_uuid(),
|
||||
gen_model)
|
||||
|
||||
@testtools.testcase.attr('negative')
|
||||
def test_create_baymodel_invalid_network_driver(self):
|
||||
self.keypairs_client.create_keypair(name='default')
|
||||
gen_model = datagen.random_baymodel_data_w_valid_keypair_and_image_id()
|
||||
gen_model = datagen.baymodel_data_with_valid_keypair_and_image_id()
|
||||
gen_model.network_driver = 'invalid_network_driver'
|
||||
self.assertRaises(
|
||||
exceptions.BadRequest,
|
||||
|
|
|
@ -33,7 +33,7 @@ class MagnumServiceTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('negative')
|
||||
def test_magnum_service_list_needs_admin(self):
|
||||
# Ensure that policy enforcement does not allow 'default' user
|
||||
(self.service_client, _) = self.get_clients_with_isolated_creds(
|
||||
(self.service_client, _) = self.get_clients_with_new_creds(
|
||||
type_of_creds='default',
|
||||
request_type='service')
|
||||
self.assertRaises(exceptions.ServerFault,
|
||||
|
@ -42,7 +42,7 @@ class MagnumServiceTest(base.BaseMagnumTest):
|
|||
@testtools.testcase.attr('positive')
|
||||
def test_magnum_service_list(self):
|
||||
# get json object
|
||||
(self.service_client, _) = self.get_clients_with_isolated_creds(
|
||||
(self.service_client, _) = self.get_clients_with_new_creds(
|
||||
type_of_creds='admin',
|
||||
request_type='service')
|
||||
resp, msvcs = self.service_client.magnum_service_list()
|
||||
|
|
|
@ -41,18 +41,7 @@ class BaseMagnumTest(base.BaseTestCase):
|
|||
if self.ic is not None:
|
||||
self.ic.clear_creds()
|
||||
|
||||
def get_clients_with_isolated_creds(self,
|
||||
name=None,
|
||||
type_of_creds="default",
|
||||
request_type=None):
|
||||
"""Creates isolated creds.
|
||||
|
||||
:param name: name, will be used for dynamic creds
|
||||
:param type_of_creds: admin, alt or default
|
||||
:param request_type: baymodel or service
|
||||
:returns: MagnumClient -- client with isolated creds.
|
||||
:returns: KeypairClient -- allows for creating of keypairs
|
||||
"""
|
||||
def get_credentials(self, name=None, type_of_creds="default"):
|
||||
if name is None:
|
||||
# Get name of test method
|
||||
name = inspect.stack()[1][3]
|
||||
|
@ -66,22 +55,58 @@ class BaseMagnumTest(base.BaseTestCase):
|
|||
admin_role=config.Config.admin_role,
|
||||
admin_creds=common_creds.get_configured_credentials(
|
||||
'identity_admin'))
|
||||
|
||||
creds = None
|
||||
if "admin" == type_of_creds:
|
||||
creds = self.ic.get_admin_creds()
|
||||
elif "alt" == type_of_creds:
|
||||
creds = self.ic.get_alt_creds()
|
||||
elif "default" == type_of_creds:
|
||||
creds = self.ic.get_primary_creds()
|
||||
else:
|
||||
creds = self.ic.self.get_credentials(type_of_creds)
|
||||
return creds
|
||||
|
||||
def get_clients(self, creds, type_of_creds, request_type):
|
||||
if "admin" == type_of_creds:
|
||||
manager_inst = manager.AdminManager(credentials=creds,
|
||||
request_type=request_type)
|
||||
elif "alt" == type_of_creds:
|
||||
creds = self.ic.get_alt_creds()
|
||||
manager_inst = manager.AltManager(credentials=creds,
|
||||
request_type=request_type)
|
||||
elif "default" == type_of_creds:
|
||||
creds = self.ic.get_primary_creds()
|
||||
manager_inst = manager.DefaultManager(credentials=creds,
|
||||
request_type=request_type)
|
||||
else:
|
||||
creds = self.ic.self.get_credentials(type_of_creds)
|
||||
manager_inst = manager.DefaultManager(credentials=creds,
|
||||
request_type=request_type)
|
||||
|
||||
# create client with isolated creds
|
||||
return (manager_inst.client, manager_inst.keypairs_client)
|
||||
|
||||
def get_clients_with_existing_creds(self,
|
||||
name=None,
|
||||
creds=None,
|
||||
type_of_creds="default",
|
||||
request_type=None):
|
||||
if creds is None:
|
||||
return self.get_clients_with_isolated_creds(name,
|
||||
type_of_creds,
|
||||
request_type)
|
||||
else:
|
||||
return self.get_clients(creds, type_of_creds, request_type)
|
||||
|
||||
def get_clients_with_new_creds(self,
|
||||
name=None,
|
||||
type_of_creds="default",
|
||||
request_type=None):
|
||||
"""Creates isolated creds.
|
||||
|
||||
:param name: name, will be used for dynamic creds
|
||||
:param type_of_creds: admin, alt or default
|
||||
:param request_type: baymodel or service
|
||||
:returns: MagnumClient -- client with isolated creds.
|
||||
:returns: KeypairClient -- allows for creating of keypairs
|
||||
"""
|
||||
creds = self.get_credentials(name, type_of_creds)
|
||||
return self.get_clients(creds, type_of_creds, request_type)
|
||||
|
|
|
@ -81,6 +81,12 @@ class Config(object):
|
|||
raise Exception('config missing keypair_id key')
|
||||
cls.keypair_id = CONF.magnum.keypair_id
|
||||
|
||||
@classmethod
|
||||
def set_flavor_id(cls, config):
|
||||
if 'flavor_id' not in CONF.magnum:
|
||||
raise Exception('config missing flavor_id key')
|
||||
cls.flavor_id = CONF.magnum.flavor_id
|
||||
|
||||
@classmethod
|
||||
def setUp(cls):
|
||||
cls.set_admin_creds(config)
|
||||
|
@ -93,3 +99,4 @@ class Config(object):
|
|||
cls.set_image_id(config)
|
||||
cls.set_nic_id(config)
|
||||
cls.set_keypair_id(config)
|
||||
cls.set_flavor_id(config)
|
||||
|
|
|
@ -14,39 +14,21 @@ import random
|
|||
import socket
|
||||
import string
|
||||
import struct
|
||||
import uuid
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
|
||||
from magnum.tests.functional.api.v1.models import bay_model
|
||||
from magnum.tests.functional.api.v1.models import baymodel_model
|
||||
from magnum.tests.functional.api.v1.models import baymodelpatch_model
|
||||
from magnum.tests.functional.api.v1.models import baypatch_model
|
||||
from magnum.tests.functional.common import config
|
||||
|
||||
|
||||
def random_uuid():
|
||||
return uuid.uuid4()
|
||||
def random_int(min_int=1, max_int=100):
|
||||
return random.randrange(min_int, max_int)
|
||||
|
||||
|
||||
def random_string(prefix='rand', n=8, suffix=''):
|
||||
"""Return a string containing random digits
|
||||
|
||||
:param prefix: the exact text to start the string. Defaults to "rand"
|
||||
:param n: the number of random digits to generate
|
||||
:param suffix: the exact text to end the string
|
||||
"""
|
||||
digits = "".join(str(random.randrange(0, 10)) for _ in range(n))
|
||||
return prefix + digits + suffix
|
||||
|
||||
|
||||
def generate_random_network():
|
||||
network_list = ["public", "private"]
|
||||
return network_list[random.randrange(0, len(network_list))]
|
||||
|
||||
|
||||
def generate_random_coe():
|
||||
coe_list = ["swarm", "kubernetes", "mesos"]
|
||||
return coe_list[random.randrange(0, len(coe_list))]
|
||||
|
||||
|
||||
def generate_random_coe_dep_network_driver(coe):
|
||||
def gen_coe_dep_network_driver(coe):
|
||||
allowed_driver_types = {
|
||||
'kubernetes': ['flannel', None],
|
||||
'swarm': ['docker', 'flannel', None],
|
||||
|
@ -56,15 +38,15 @@ def generate_random_coe_dep_network_driver(coe):
|
|||
return driver_types[random.randrange(0, len(driver_types))]
|
||||
|
||||
|
||||
def generate_random_port():
|
||||
return random.randrange(49152, 65535)
|
||||
def gen_random_port():
|
||||
return random_int(49152, 65535)
|
||||
|
||||
|
||||
def generate_random_docker_volume_size():
|
||||
return random.randrange(1, 3)
|
||||
def gen_docker_volume_size(min_int=1, max_int=3):
|
||||
return random_int(min_int, max_int)
|
||||
|
||||
|
||||
def generate_fake_ssh_pubkey():
|
||||
def gen_fake_ssh_pubkey():
|
||||
chars = "".join(
|
||||
random.choice(string.ascii_uppercase +
|
||||
string.ascii_letters + string.digits + '/+=')
|
||||
|
@ -72,11 +54,27 @@ def generate_fake_ssh_pubkey():
|
|||
return "ssh-rsa " + chars
|
||||
|
||||
|
||||
def generate_random_ip():
|
||||
def gen_random_ip():
|
||||
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||
|
||||
|
||||
def random_baymodel_data(keypair_id=random_string(), image_id=random_string()):
|
||||
def gen_url(scheme="http", domain="example.com", port=80):
|
||||
return "%s://%s:%s" % (scheme, domain, port)
|
||||
|
||||
|
||||
def gen_http_proxy():
|
||||
return gen_url(port=gen_random_port())
|
||||
|
||||
|
||||
def gen_https_proxy():
|
||||
return gen_url(scheme="https", port=gen_random_port())
|
||||
|
||||
|
||||
def gen_no_proxy():
|
||||
return ",".join(gen_random_ip() for x in range(3))
|
||||
|
||||
|
||||
def baymodel_data(**kwargs):
|
||||
"""Generates random baymodel data
|
||||
|
||||
Keypair and image id cannot be random for the baymodel to be valid due to
|
||||
|
@ -88,33 +86,28 @@ def random_baymodel_data(keypair_id=random_string(), image_id=random_string()):
|
|||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
coe = generate_random_coe()
|
||||
data = {
|
||||
"name": random_string(),
|
||||
"image_id": image_id,
|
||||
"flavor_id": random_string(),
|
||||
"master_flavor_id": random_string(),
|
||||
"dns_nameserver": generate_random_ip(),
|
||||
"keypair_id": keypair_id,
|
||||
"external_network_id": str(random_uuid()),
|
||||
"fixed_network": generate_random_network(),
|
||||
"apiserver_port": generate_random_port(),
|
||||
"docker_volume_size": generate_random_docker_volume_size(),
|
||||
"cluster_distro": random_string(),
|
||||
"ssh_authorized_key": generate_fake_ssh_pubkey(),
|
||||
"coe": coe,
|
||||
"http_proxy": "http://proxy.com:%s" % generate_random_port(),
|
||||
"https_proxy": "https://proxy.com:%s" % generate_random_port(),
|
||||
"no_proxy": ",".join(generate_random_ip() for x in range(3)),
|
||||
"network_driver": generate_random_coe_dep_network_driver(coe),
|
||||
"labels": {"K1": "V1", "K2": "V2"},
|
||||
"name": data_utils.rand_name('bay'),
|
||||
"coe": "swarm",
|
||||
"tls_disabled": False,
|
||||
"network_driver": None,
|
||||
"docker_volume_size": 1,
|
||||
"labels": {},
|
||||
"fixed_network": "192.168.0.0/24",
|
||||
"dns_nameserver": "8.8.8.8",
|
||||
"flavor_id": data_utils.rand_name('bay'),
|
||||
"external_network_id": str(data_utils.rand_uuid()),
|
||||
"keypair_id": data_utils.rand_name('bay'),
|
||||
"image_id": data_utils.rand_name('bay')
|
||||
}
|
||||
|
||||
data.update(kwargs)
|
||||
model = baymodel_model.BayModelEntity.from_dict(data)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def random_baymodel_name_patch_data(name=random_string()):
|
||||
def baymodel_name_patch_data(name=data_utils.rand_name('bay')):
|
||||
"""Generates random baymodel patch data
|
||||
|
||||
:param name: name to replace in patch
|
||||
|
@ -129,29 +122,150 @@ def random_baymodel_name_patch_data(name=random_string()):
|
|||
return baymodelpatch_model.BayModelPatchCollection.from_dict(data)
|
||||
|
||||
|
||||
def random_baymodel_data_w_valid_keypair_and_image_id():
|
||||
def baymodel_data_with_valid_keypair_and_image_id():
|
||||
"""Generates random baymodel data with valid keypair and image
|
||||
|
||||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
return random_baymodel_data(keypair_id=config.Config.keypair_id,
|
||||
return baymodel_data(keypair_id=config.Config.keypair_id,
|
||||
image_id=config.Config.image_id)
|
||||
|
||||
|
||||
def random_baymodel_data_w_valid_keypair():
|
||||
def baymodel_data_with_valid_keypair():
|
||||
"""Generates random baymodel data with valid keypair
|
||||
|
||||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
return random_baymodel_data(keypair_id=config.Config.keypair_id)
|
||||
return baymodel_data(keypair_id=config.Config.keypair_id)
|
||||
|
||||
|
||||
def random_baymodel_data_w_valid_image_id():
|
||||
def baymodel_valid_data_with_specific_coe(coe):
|
||||
"""Generates random baymodel data with valid keypair and image
|
||||
|
||||
:param coe: coe
|
||||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
return baymodel_data(keypair_id=config.Config.keypair_id,
|
||||
image_id=config.Config.image_id, coe=coe)
|
||||
|
||||
|
||||
def baymodel_data_with_valid_image_id():
|
||||
"""Generates random baymodel data with valid image
|
||||
|
||||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
return random_baymodel_data(image_id=config.Config.image_id)
|
||||
return baymodel_data(image_id=config.Config.image_id)
|
||||
|
||||
|
||||
def valid_swarm_baymodel():
|
||||
"""Generates a valid swarm baymodel with valid data
|
||||
|
||||
:returns: BayModelEntity with generated data
|
||||
"""
|
||||
|
||||
return baymodel_data(image_id=config.Config.image_id,
|
||||
fixed_network="192.168.0.0/24",
|
||||
flavor_id=config.Config.flavor_id, public=False,
|
||||
dns_nameserver="8.8.8.8", master_flavor_id=None,
|
||||
keypair_id=config.Config.keypair_id, coe="swarm",
|
||||
docker_volume_size=2, cluster_distro=None,
|
||||
ssh_authorized_key=None, external_network_id="public",
|
||||
http_proxy=None, https_proxy=None, no_proxy=None,
|
||||
network_driver=None, labels={}, tls_disabled=False)
|
||||
|
||||
|
||||
def bay_data(name=data_utils.rand_name('bay'),
|
||||
baymodel_id=data_utils.rand_uuid(),
|
||||
node_count=random_int(1, 5), discovery_url=gen_random_ip(),
|
||||
bay_create_timeout=random_int(1, 30),
|
||||
master_count=random_int(1, 5)):
|
||||
"""Generates random bay data
|
||||
|
||||
BayModel_id cannot be random for the bay to be valid due to
|
||||
validations for the presence of baymodel prior to baymodel
|
||||
creation.
|
||||
|
||||
:param name: bay name (must be unique)
|
||||
:param baymodel_id: baymodel unique id (must already exist)
|
||||
:param node_count: number of agents for bay
|
||||
:param discovery_url: url provided for node discovery
|
||||
:param bay_create_timeout: timeout in minutes for bay create
|
||||
:param master_count: number of master nodes for the bay
|
||||
:returns: BayEntity with generated data
|
||||
"""
|
||||
|
||||
data = {
|
||||
"name": name,
|
||||
"baymodel_id": baymodel_id,
|
||||
"node_count": node_count,
|
||||
"discovery_url": None,
|
||||
"bay_create_timeout": bay_create_timeout,
|
||||
"master_count": master_count
|
||||
}
|
||||
model = bay_model.BayEntity.from_dict(data)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def valid_bay_data(baymodel_id, name=data_utils.rand_name('bay'), node_count=2,
|
||||
master_count=1, bay_create_timeout=None):
|
||||
"""Generates random bay data with valid
|
||||
|
||||
:param baymodel_id: baymodel unique id that already exists
|
||||
:param name: bay name (must be unique)
|
||||
:param node_count: number of agents for bay
|
||||
:returns: BayEntity with generated data
|
||||
"""
|
||||
|
||||
return bay_data(baymodel_id=baymodel_id, name=name,
|
||||
master_count=master_count, node_count=node_count,
|
||||
bay_create_timeout=bay_create_timeout)
|
||||
|
||||
|
||||
def bay_name_patch_data(name=data_utils.rand_name('bay')):
|
||||
"""Generates random baymodel patch data
|
||||
|
||||
:param name: name to replace in patch
|
||||
:returns: BayPatchCollection with generated data
|
||||
"""
|
||||
|
||||
data = [{
|
||||
"path": "/name",
|
||||
"value": name,
|
||||
"op": "replace"
|
||||
}]
|
||||
return baypatch_model.BayPatchCollection.from_dict(data)
|
||||
|
||||
|
||||
def bay_api_addy_patch_data(address='0.0.0.0'):
|
||||
"""Generates random bay patch data
|
||||
|
||||
:param name: name to replace in patch
|
||||
:returns: BayPatchCollection with generated data
|
||||
"""
|
||||
|
||||
data = [{
|
||||
"path": "/api_address",
|
||||
"value": address,
|
||||
"op": "replace"
|
||||
}]
|
||||
return baypatch_model.BayPatchCollection.from_dict(data)
|
||||
|
||||
|
||||
def bay_node_count_patch_data(node_count=2):
|
||||
"""Generates random bay patch data
|
||||
|
||||
:param name: name to replace in patch
|
||||
:returns: BayPatchCollection with generated data
|
||||
"""
|
||||
|
||||
data = [{
|
||||
"path": "/node_count",
|
||||
"value": node_count,
|
||||
"op": "replace"
|
||||
}]
|
||||
return baypatch_model.BayPatchCollection.from_dict(data)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
from tempest import clients
|
||||
from tempest.common import credentials_factory as common_creds
|
||||
|
||||
from magnum.tests.functional.api.v1.clients import bay_client
|
||||
from magnum.tests.functional.api.v1.clients import baymodel_client
|
||||
from magnum.tests.functional.api.v1.clients import magnum_service_client
|
||||
from magnum.tests.functional.common import client
|
||||
|
@ -27,6 +28,8 @@ class Manager(clients.Manager):
|
|||
super(Manager, self).__init__(credentials, 'container')
|
||||
if request_type == 'baymodel':
|
||||
self.client = baymodel_client.BayModelClient(self.auth_provider)
|
||||
elif request_type == 'bay':
|
||||
self.client = bay_client.BayClient(self.auth_provider)
|
||||
elif request_type == 'service':
|
||||
self.client = magnum_service_client.MagnumServiceClient(
|
||||
self.auth_provider)
|
||||
|
|
|
@ -82,13 +82,15 @@ def parameterized(data):
|
|||
|
||||
|
||||
def wait_for_condition(condition, interval=1, timeout=40):
|
||||
start_time = time.time()
|
||||
end_time = time.time() + timeout
|
||||
while time.time() < end_time:
|
||||
result = condition()
|
||||
if result:
|
||||
return result
|
||||
time.sleep(interval)
|
||||
raise Exception("Timed out after {0} seconds".format(timeout))
|
||||
raise Exception("Timed out after %s seconds. Started " +
|
||||
"on %s and ended on %s" % (timeout, start_time, end_time))
|
||||
|
||||
|
||||
def memoized(func):
|
||||
|
|
Loading…
Reference in New Issue