Remove flavor API
Uses should get flavor from Nova. Change-Id: Id26d71811f94cdcf4ff188c021d4ed5613eeb8cd
This commit is contained in:
parent
feb982547a
commit
910519127d
@ -6,17 +6,7 @@
|
||||
"version": "5.7"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/9f8dd5eacb074c9f87d2d822c9092aa5/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "b76a6a76-748b-4064-adec-4c9e6c9abd68",
|
||||
"links": [
|
||||
|
@ -17,17 +17,7 @@
|
||||
"version": "5.7"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/9f8dd5eacb074c9f87d2d822c9092aa5/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "7de1bed8-6983-4d46-9a52-0abfbb0d27a2",
|
||||
"ip": [
|
||||
@ -65,17 +55,7 @@
|
||||
"message": "Failed to create User port for instance b76a6a76-748b-4064-adec-4c9e6c9abd68"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/9f8dd5eacb074c9f87d2d822c9092aa5/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "b76a6a76-748b-4064-adec-4c9e6c9abd68",
|
||||
"links": [
|
||||
|
@ -6,17 +6,7 @@
|
||||
"version": "5.7"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/9f8dd5eacb074c9f87d2d822c9092aa5/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "7de1bed8-6983-4d46-9a52-0abfbb0d27a2",
|
||||
"ip": [
|
||||
|
@ -20,17 +20,7 @@
|
||||
"deleted_at": null,
|
||||
"encrypted_rpc_messaging": true,
|
||||
"flavor": {
|
||||
"id": "d2",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/2afa58fd5db34fd8b7b659d997a5341f/flavors/d2",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/d2",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "d2"
|
||||
},
|
||||
"id": "7de1bed8-6983-4d46-9a52-0abfbb0d27a2",
|
||||
"ip": [
|
||||
@ -82,17 +72,7 @@
|
||||
"message": "Failed to create User port for instance b76a6a76-748b-4064-adec-4c9e6c9abd68"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/2afa58fd5db34fd8b7b659d997a5341f/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "b76a6a76-748b-4064-adec-4c9e6c9abd68",
|
||||
"links": [
|
||||
|
@ -19,17 +19,7 @@
|
||||
"deleted_at": null,
|
||||
"encrypted_rpc_messaging": true,
|
||||
"flavor": {
|
||||
"id": "d2",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/2afa58fd5db34fd8b7b659d997a5341f/flavors/d2",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/d2",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "d2"
|
||||
},
|
||||
"guest_status": {
|
||||
"state_description": "healthy"
|
||||
|
@ -16,17 +16,7 @@
|
||||
"version": "5.7"
|
||||
},
|
||||
"flavor": {
|
||||
"id": "6",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/v1.0/9f8dd5eacb074c9f87d2d822c9092aa5/flavors/6",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://127.0.0.1:8779/flavors/6",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
"id": "6"
|
||||
},
|
||||
"id": "7de1bed8-6983-4d46-9a52-0abfbb0d27a2",
|
||||
"ip": [
|
||||
|
@ -658,4 +658,4 @@ In the API response, note that the additional key
|
||||
|
||||
::
|
||||
|
||||
RESP BODY: {"instance": {"status": "ACTIVE", "updated": "2017-01-09T18:29:00", "name": "m10", "links": [{"href": "https://192.168.126.130:8779/v1.0/56cca8484d3e48869126ada4f355c284/instances/514ef051-0bf7-48a5-adcf-071d4a6625fb", "rel": "self"}, {"href": "https://192.168.126.130:8779/instances/514ef051-0bf7-48a5-adcf-071d4a6625fb", "rel": "bookmark"}], "created": "2017-01-09T18:28:56", "region": "RegionOne", "server_id": "2452263e-3d33-48ec-8f24-2851fe74db28", "id": "514ef051-0bf7-48a5-adcf-071d4a6625fb", "volume": {"used": 0.11, "size": 3}, "volume_id": "cee2e17b-80fa-48e5-a488-da8b7809373a", "flavor": {"id": "25", "links": [{"href": "https://192.168.126.130:8779/v1.0/56cca8484d3e48869126ada4f355c284/flavors/25", "rel": "self"}, {"href": "https://192.168.126.130:8779/flavors/25", "rel": "bookmark"}]}, "datastore": {"version": "5.6", "type": "mysql"}, "encrypted_rpc_messaging": false}}
|
||||
RESP BODY: {"instance": {"status": "ACTIVE", "updated": "2017-01-09T18:29:00", "name": "m10", "links": [{"href": "https://192.168.126.130:8779/v1.0/56cca8484d3e48869126ada4f355c284/instances/514ef051-0bf7-48a5-adcf-071d4a6625fb", "rel": "self"}, {"href": "https://192.168.126.130:8779/instances/514ef051-0bf7-48a5-adcf-071d4a6625fb", "rel": "bookmark"}], "created": "2017-01-09T18:28:56", "region": "RegionOne", "server_id": "2452263e-3d33-48ec-8f24-2851fe74db28", "id": "514ef051-0bf7-48a5-adcf-071d4a6625fb", "volume": {"used": 0.11, "size": 3}, "volume_id": "cee2e17b-80fa-48e5-a488-da8b7809373a", "flavor": {"id": "25"}, "datastore": {"version": "5.6", "type": "mysql"}, "encrypted_rpc_messaging": false}}
|
||||
|
@ -113,11 +113,7 @@ Create and use incremental backups
|
||||
| created | 2014-03-19T14:10:56 |
|
||||
| datastore | {u'version': u'mysql-5.5', u'type': u'mysql'} |
|
||||
| datastore_version | mysql-5.5 |
|
||||
| flavor | {u'id': u'10', u'links': |
|
||||
| | [{u'href': u'https://10.125.1.135:8779/v1.0/ |
|
||||
| | 626734041baa4254ae316de52a20b390/flavors/10', u'rel': |
|
||||
| | u'self'}, {u'href': u'https://10.125.1.135:8779/ |
|
||||
| | flavors/10', u'rel': u'bookmark'}]} |
|
||||
| flavor | {u'id': u'10'} |
|
||||
| id | a3680953-eea9-4cf2-918b-5b8e49d7e1b3 |
|
||||
| name | guest2 |
|
||||
| status | BUILD |
|
||||
|
@ -102,10 +102,7 @@ class ClusterView(object):
|
||||
return None
|
||||
|
||||
def _build_flavor_info(self, flavor_id):
|
||||
return {
|
||||
"id": flavor_id,
|
||||
"links": create_links("flavors", self.req, flavor_id)
|
||||
}
|
||||
return {"id": flavor_id}
|
||||
|
||||
|
||||
class ClusterInstanceDetailView(InstanceDetailView):
|
||||
|
@ -20,7 +20,6 @@ from trove.common import wsgi
|
||||
from trove.configuration.service import ConfigurationsController
|
||||
from trove.configuration.service import ParametersController
|
||||
from trove.datastore.service import DatastoreController
|
||||
from trove.flavor.service import FlavorController
|
||||
from trove.instance.service import InstanceController
|
||||
from trove.limits.service import LimitsController
|
||||
from trove.module.service import ModuleController
|
||||
@ -35,7 +34,6 @@ class API(wsgi.Router):
|
||||
self._instance_router(mapper)
|
||||
self._cluster_router(mapper)
|
||||
self._datastore_router(mapper)
|
||||
self._flavor_router(mapper)
|
||||
self._versions_router(mapper)
|
||||
self._limits_router(mapper)
|
||||
self._backups_router(mapper)
|
||||
@ -168,17 +166,6 @@ class API(wsgi.Router):
|
||||
action="delete",
|
||||
conditions={'method': ['DELETE']})
|
||||
|
||||
def _flavor_router(self, mapper):
|
||||
flavor_resource = FlavorController().create_resource()
|
||||
mapper.connect("/{tenant_id}/flavors",
|
||||
controller=flavor_resource,
|
||||
action="index",
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect("/{tenant_id}/flavors/{id}",
|
||||
controller=flavor_resource,
|
||||
action="show",
|
||||
conditions={'method': ['GET']})
|
||||
|
||||
def _limits_router(self, mapper):
|
||||
limits_resource = LimitsController().create_resource()
|
||||
mapper.connect("/{tenant_id}/limits",
|
||||
|
@ -1,54 +0,0 @@
|
||||
# Copyright 2010-2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import six
|
||||
|
||||
from trove.common import exception
|
||||
from trove.common import policy
|
||||
from trove.common import wsgi
|
||||
from trove.flavor import models
|
||||
from trove.flavor import views
|
||||
|
||||
|
||||
class FlavorController(wsgi.Controller):
|
||||
"""Controller for flavor functionality."""
|
||||
|
||||
def show(self, req, tenant_id, id):
|
||||
"""Return a single flavor."""
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
self._validate_flavor_id(id)
|
||||
flavor = models.Flavor(context=context, flavor_id=id)
|
||||
# Flavors do not bind to a particular tenant.
|
||||
# Only authorize the current tenant.
|
||||
policy.authorize_on_tenant(context, 'flavor:show')
|
||||
# Pass in the request to build accurate links.
|
||||
return wsgi.Result(views.FlavorView(flavor, req).data(), 200)
|
||||
|
||||
def index(self, req, tenant_id):
|
||||
"""Return all flavors."""
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
policy.authorize_on_tenant(context, 'flavor:index')
|
||||
flavors = models.Flavors(context=context)
|
||||
return wsgi.Result(views.FlavorsView(flavors, req).data(), 200)
|
||||
|
||||
def _validate_flavor_id(self, id):
|
||||
if isinstance(id, six.string_types):
|
||||
return
|
||||
try:
|
||||
if int(id) != float(id):
|
||||
raise exception.NotFound(uuid=id)
|
||||
except ValueError:
|
||||
raise exception.NotFound(uuid=id)
|
@ -72,13 +72,8 @@ class InstanceView(object):
|
||||
def _build_flavor_info(self):
|
||||
return {
|
||||
"id": self.instance.flavor_id,
|
||||
"links": self._build_flavor_links()
|
||||
}
|
||||
|
||||
def _build_flavor_links(self):
|
||||
return create_links("flavors", self.req,
|
||||
self.instance.flavor_id)
|
||||
|
||||
def _build_master_info(self):
|
||||
return {
|
||||
"id": self.instance.slave_of_id,
|
||||
|
@ -36,6 +36,7 @@ from trove.common.utils import poll_until
|
||||
from trove.datastore import models as datastore_models
|
||||
from trove import tests
|
||||
from trove.tests.config import CONFIG
|
||||
from trove.tests import util
|
||||
from trove.tests.util.check import AttrCheck
|
||||
from trove.tests.util import create_dbaas_client
|
||||
from trove.tests.util import test_config
|
||||
@ -88,6 +89,7 @@ class InstanceTestInfo(object):
|
||||
self.user_context = None # A regular user context
|
||||
self.users = None # The users created on the instance.
|
||||
self.consumer = create_usage_verifier()
|
||||
self.flavors = None # The cache of Nova flavors.
|
||||
|
||||
def find_default_flavor(self):
|
||||
if EPHEMERAL_SUPPORT:
|
||||
@ -96,15 +98,17 @@ class InstanceTestInfo(object):
|
||||
else:
|
||||
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
|
||||
|
||||
flavors = self.dbaas.find_flavors_by_name(flavor_name)
|
||||
assert_equal(len(flavors), 1,
|
||||
"Number of flavors with name '%s' "
|
||||
"found was '%d'." % (flavor_name, len(flavors)))
|
||||
flavor = None
|
||||
flavor_href = None
|
||||
|
||||
flavor = flavors[0]
|
||||
flavor_href = self.dbaas.find_flavor_self_href(flavor)
|
||||
assert_true(flavor_href is not None,
|
||||
"Flavor href '%s' not found!" % flavor_name)
|
||||
for item in self.flavors:
|
||||
if item.name == flavor_name:
|
||||
flavor = item
|
||||
flavor_href = item.id
|
||||
break
|
||||
|
||||
asserts.assert_is_not_none(flavor)
|
||||
asserts.assert_is_not_none(flavor_href)
|
||||
|
||||
return flavor, flavor_href
|
||||
|
||||
@ -190,11 +194,10 @@ class CheckInstance(AttrCheck):
|
||||
if 'flavor' not in self.instance:
|
||||
self.fail("'flavor' not found in instance.")
|
||||
else:
|
||||
allowed_attrs = ['id', 'links']
|
||||
allowed_attrs = ['id']
|
||||
self.contains_allowed_attrs(
|
||||
self.instance['flavor'], allowed_attrs,
|
||||
msg="Flavor")
|
||||
self.links(self.instance['flavor']['links'])
|
||||
|
||||
def datastore(self):
|
||||
if 'datastore' not in self.instance:
|
||||
@ -315,6 +318,10 @@ class TestInstanceSetup(object):
|
||||
instance_info.user = CONFIG.users.find_user(reqs)
|
||||
|
||||
instance_info.dbaas = create_dbaas_client(instance_info.user)
|
||||
|
||||
instance_info.nova_client = util.create_nova_client(instance_info.user)
|
||||
instance_info.flavors = instance_info.nova_client.flavors.list()
|
||||
|
||||
global dbaas
|
||||
dbaas = instance_info.dbaas
|
||||
|
||||
@ -563,10 +570,16 @@ class CreateInstanceFail(object):
|
||||
instance_name = "instance-failure-with-no-ephemeral-flavor"
|
||||
databases = []
|
||||
flavor_name = CONFIG.values.get('instance_flavor_name', 'm1.tiny')
|
||||
flavors = dbaas.find_flavors_by_name(flavor_name)
|
||||
|
||||
flavor_id = None
|
||||
for item in instance_info.flavors:
|
||||
if item.name == flavor_name:
|
||||
flavor_id = item.id
|
||||
|
||||
asserts.assert_is_not_none(flavor_id)
|
||||
|
||||
assert_raises(exceptions.BadRequest, dbaas.instances.create,
|
||||
instance_name, flavors[0].id, None, databases,
|
||||
instance_name, flavor_id, None, databases,
|
||||
nics=instance_info.nics)
|
||||
assert_equal(400, dbaas.last_http_code)
|
||||
|
||||
|
@ -367,11 +367,6 @@ class ResizeInstanceTest(ActionTestBase):
|
||||
def flavor_id(self):
|
||||
return instance_info.dbaas_flavor_href
|
||||
|
||||
def get_flavor_href(self, flavor_id=2):
|
||||
res = instance_info.dbaas.find_flavor_and_self_href(flavor_id)
|
||||
_, dbaas_flavor_href = res
|
||||
return dbaas_flavor_href
|
||||
|
||||
def wait_for_resize(self):
|
||||
def is_finished_resizing():
|
||||
instance = self.instance
|
||||
@ -399,7 +394,12 @@ class ResizeInstanceTest(ActionTestBase):
|
||||
def test_instance_resize_to_ephemeral_in_volume_support_should_fail(self):
|
||||
flavor_name = CONFIG.values.get('instance_bigger_eph_flavor_name',
|
||||
'eph.rd-smaller')
|
||||
flavors = self.dbaas.find_flavors_by_name(flavor_name)
|
||||
flavor_id = None
|
||||
for item in instance_info.flavors:
|
||||
if item.name == flavor_name:
|
||||
flavor_id = item.id
|
||||
|
||||
asserts.assert_is_not_none(flavor_id)
|
||||
|
||||
def is_active():
|
||||
return self.instance.status in CONFIG.running_status
|
||||
@ -409,36 +409,42 @@ class ResizeInstanceTest(ActionTestBase):
|
||||
|
||||
asserts.assert_raises(HTTPNotImplemented,
|
||||
self.dbaas.instances.resize_instance,
|
||||
self.instance_id, flavors[0].id)
|
||||
self.instance_id, flavor_id)
|
||||
|
||||
@test(enabled=EPHEMERAL_SUPPORT)
|
||||
def test_instance_resize_to_non_ephemeral_flavor_should_fail(self):
|
||||
flavor_name = CONFIG.values.get('instance_bigger_flavor_name',
|
||||
'm1-small')
|
||||
flavors = self.dbaas.find_flavors_by_name(flavor_name)
|
||||
flavor_id = None
|
||||
for item in instance_info.flavors:
|
||||
if item.name == flavor_name:
|
||||
flavor_id = item.id
|
||||
|
||||
asserts.assert_is_not_none(flavor_id)
|
||||
asserts.assert_raises(BadRequest, self.dbaas.instances.resize_instance,
|
||||
self.instance_id, flavors[0].id)
|
||||
self.instance_id, flavor_id)
|
||||
|
||||
def obtain_flavor_ids(self):
|
||||
old_id = self.instance.flavor['id']
|
||||
self.expected_old_flavor_id = old_id
|
||||
res = instance_info.dbaas.find_flavor_and_self_href(old_id)
|
||||
self.expected_dbaas_flavor, _ = res
|
||||
if EPHEMERAL_SUPPORT:
|
||||
flavor_name = CONFIG.values.get('instance_bigger_eph_flavor_name',
|
||||
'eph.rd-smaller')
|
||||
else:
|
||||
flavor_name = CONFIG.values.get('instance_bigger_flavor_name',
|
||||
'm1.small')
|
||||
flavors = self.dbaas.find_flavors_by_name(flavor_name)
|
||||
asserts.assert_equal(len(flavors), 1,
|
||||
"Number of flavors with name '%s' "
|
||||
"found was '%d'." % (flavor_name,
|
||||
len(flavors)))
|
||||
flavor = flavors[0]
|
||||
|
||||
new_flavor = None
|
||||
for item in instance_info.flavors:
|
||||
if item.name == flavor_name:
|
||||
new_flavor = item
|
||||
break
|
||||
|
||||
asserts.assert_is_not_none(new_flavor)
|
||||
|
||||
self.old_dbaas_flavor = instance_info.dbaas_flavor
|
||||
instance_info.dbaas_flavor = flavor
|
||||
self.expected_new_flavor_id = flavor.id
|
||||
instance_info.dbaas_flavor = new_flavor
|
||||
self.expected_new_flavor_id = new_flavor.id
|
||||
|
||||
@test(depends_on=[test_instance_resize_same_size_should_fail])
|
||||
def test_status_changed_to_resize(self):
|
||||
@ -447,14 +453,14 @@ class ResizeInstanceTest(ActionTestBase):
|
||||
self.obtain_flavor_ids()
|
||||
self.dbaas.instances.resize_instance(
|
||||
self.instance_id,
|
||||
self.get_flavor_href(flavor_id=self.expected_new_flavor_id))
|
||||
self.expected_new_flavor_id)
|
||||
asserts.assert_equal(202, self.dbaas.last_http_code)
|
||||
|
||||
# (WARNING) IF THE RESIZE IS WAY TOO FAST THIS WILL FAIL
|
||||
assert_unprocessable(
|
||||
self.dbaas.instances.resize_instance,
|
||||
self.instance_id,
|
||||
self.get_flavor_href(flavor_id=self.expected_new_flavor_id))
|
||||
self.expected_new_flavor_id)
|
||||
|
||||
@test(depends_on=[test_status_changed_to_resize])
|
||||
@time_out(TIME_OUT_TIME)
|
||||
@ -493,9 +499,8 @@ class ResizeInstanceTest(ActionTestBase):
|
||||
|
||||
@test(depends_on=[test_make_sure_mysql_is_running_after_resize])
|
||||
def test_instance_has_new_flavor_after_resize(self):
|
||||
actual = self.get_flavor_href(self.instance.flavor['id'])
|
||||
expected = self.get_flavor_href(flavor_id=self.expected_new_flavor_id)
|
||||
asserts.assert_equal(actual, expected)
|
||||
actual = self.instance.flavor['id']
|
||||
asserts.assert_equal(actual, self.expected_new_flavor_id)
|
||||
|
||||
|
||||
@test(depends_on_classes=[ResizeInstanceTest],
|
||||
|
@ -1,293 +0,0 @@
|
||||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
from proboscis.asserts import fail
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from troveclient.compat.client import TroveHTTPClient
|
||||
|
||||
from trove.tests.config import CONFIG
|
||||
|
||||
print_req = True
|
||||
|
||||
|
||||
def shorten_url(url):
|
||||
parsed = urlparse(url)
|
||||
if parsed.query:
|
||||
method_url = parsed.path + '?' + parsed.query
|
||||
else:
|
||||
method_url = parsed.path
|
||||
return method_url
|
||||
|
||||
|
||||
class SnippetWriter(object):
|
||||
|
||||
def __init__(self, conf, get_replace_list):
|
||||
self.conf = conf
|
||||
self.get_replace_list = get_replace_list
|
||||
|
||||
def output_request(self, user_details, name, url, output_headers, body,
|
||||
content_type, method, static_auth_token=True):
|
||||
headers = []
|
||||
parsed = urlparse(url)
|
||||
method_url = shorten_url(url)
|
||||
headers.append("%s %s HTTP/1.1" % (method, method_url))
|
||||
headers.append("User-Agent: %s" % output_headers['User-Agent'])
|
||||
headers.append("Host: %s" % parsed.netloc)
|
||||
# static_auth_token option for documentation purposes
|
||||
if static_auth_token:
|
||||
output_token = '87c6033c-9ff6-405f-943e-2deb73f278b7'
|
||||
else:
|
||||
output_token = output_headers['X-Auth-Token']
|
||||
headers.append("X-Auth-Token: %s" % output_token)
|
||||
headers.append("Accept: %s" % output_headers['Accept'])
|
||||
print("OUTPUT HEADERS: %s" % output_headers)
|
||||
headers.append("Content-Type: %s" % output_headers['Content-Type'])
|
||||
self.write_file(user_details, name, "-%s-http.txt" % content_type, url,
|
||||
method, "request", output='\n'.join(headers))
|
||||
|
||||
pretty_body = self.format_body(body, content_type)
|
||||
self.write_file(user_details, name, ".%s" % content_type, url,
|
||||
method, "request", output=pretty_body)
|
||||
|
||||
def output_response(self, user_details, name, content_type, url, method,
|
||||
resp, body):
|
||||
version = "1.1" # if resp.version == 11 else "1.0"
|
||||
lines = [
|
||||
["HTTP/%s %s %s" % (version, resp.status, resp.reason)],
|
||||
["Content-Type: %s" % resp['content-type']],
|
||||
]
|
||||
if 'via' in resp:
|
||||
lines.append(["Via: %s" % resp['via']])
|
||||
lines.append(["Content-Length: %s" % resp['content-length']])
|
||||
lines.append(["Date: Mon, 18 Mar 2013 19:09:17 GMT"])
|
||||
if 'server' in resp:
|
||||
lines.append(["Server: %s" % resp["server"]])
|
||||
|
||||
new_lines = [x[0] for x in lines]
|
||||
joined_lines = '\n'.join(new_lines)
|
||||
|
||||
self.write_file(user_details, name, "-%s-http.txt" % content_type, url,
|
||||
method, "response", output=joined_lines)
|
||||
|
||||
if body:
|
||||
pretty_body = self.format_body(body, content_type)
|
||||
self.write_file(user_details, name, ".%s" % content_type, url,
|
||||
method, "response", output=pretty_body)
|
||||
|
||||
def format_body(self, body, content_type):
|
||||
assert content_type == 'json'
|
||||
try:
|
||||
if self.conf['replace_dns_hostname']:
|
||||
before = r'\"hostname\": \"[a-zA-Z0-9-_\.]*\"'
|
||||
after = '\"hostname\": \"%s\"' % self.conf[
|
||||
'replace_dns_hostname']
|
||||
body = re.sub(before, after, body)
|
||||
return json.dumps(json.loads(body), sort_keys=True, indent=4)
|
||||
except Exception:
|
||||
return body or ''
|
||||
|
||||
def write_request_file(self, user_details, name, content_type, url, method,
|
||||
req_headers, request_body):
|
||||
if print_req:
|
||||
print("\t%s req url:%s" % (content_type, url))
|
||||
print("\t%s req method:%s" % (content_type, method))
|
||||
print("\t%s req headers:%s" % (content_type, req_headers))
|
||||
print("\t%s req body:%s" % (content_type, request_body))
|
||||
self.output_request(user_details, name, url, req_headers, request_body,
|
||||
content_type, method)
|
||||
|
||||
def write_response_file(self, user_details, name, content_type, url,
|
||||
method, resp, resp_content):
|
||||
if print_req:
|
||||
print("\t%s resp:%s" % (content_type, resp))
|
||||
print("\t%s resp content:%s" % (content_type, resp_content))
|
||||
self.output_response(user_details, name, content_type, url, method,
|
||||
resp, resp_content)
|
||||
|
||||
def write_file(self, user_details, name, content_type, url, method,
|
||||
in_or_out, output):
|
||||
output = output.replace(user_details['tenant'], '1234')
|
||||
if self.conf['replace_host']:
|
||||
output = output.replace(user_details['api_url'],
|
||||
self.conf['replace_host'])
|
||||
pre_host_port = urlparse(user_details['service_url']).netloc
|
||||
post_host = urlparse(self.conf['replace_host']).netloc
|
||||
output = output.replace(pre_host_port, post_host)
|
||||
output = output.replace("fake_host", "hostname")
|
||||
output = output.replace("FAKE_", "")
|
||||
for resource in self.get_replace_list():
|
||||
output = output.replace(str(resource[0]), str(resource[1]))
|
||||
filename = "%s/db-%s-%s%s" % (self.conf['directory'],
|
||||
name.replace('_', '-'), in_or_out,
|
||||
content_type)
|
||||
self._write_file(filename, output)
|
||||
|
||||
def _write_file(self, filename, output):
|
||||
empty = len(output.strip()) == 0
|
||||
# Manipulate actual data to appease doc niceness checks
|
||||
actual = [line.rstrip() for line in output.split("\n")]
|
||||
if not empty and actual[len(actual) - 1] != '':
|
||||
actual.append("")
|
||||
|
||||
def goofy_diff(a, b):
|
||||
diff = []
|
||||
for i in range(len(a)):
|
||||
if i < len(b):
|
||||
if a[i].rstrip() != b[i].rstrip():
|
||||
diff.append('Expected line %d :%s\n'
|
||||
' Actual line %d :%s'
|
||||
% (i + 1, a[i], i + 1, b[i]))
|
||||
else:
|
||||
diff.append("Expected line %d :%s" % (i + 1, a[i]))
|
||||
for j in range(len(b) - len(a)):
|
||||
i2 = len(a) + j
|
||||
diff.append(" Actual line %d :%s" % (i2 + 1, b[i2]))
|
||||
return diff
|
||||
|
||||
def write_actual_file():
|
||||
# Always write the file.
|
||||
with open(filename, "w") as file:
|
||||
for line in actual:
|
||||
file.write("%s\n" % line)
|
||||
|
||||
def assert_output_matches():
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, 'r') as original_file:
|
||||
original = original_file.read()
|
||||
if empty:
|
||||
fail('Error: output missing in new snippet generation '
|
||||
'for %s. Old content follows:\n"""%s"""'
|
||||
% (filename, original))
|
||||
elif filename.endswith('.json'):
|
||||
assert_json_matches(original)
|
||||
else:
|
||||
assert_file_matches(original)
|
||||
elif not empty:
|
||||
fail('Error: new file necessary where there was no file '
|
||||
'before. Filename=%s\nContent follows:\n"""%s"""'
|
||||
% (filename, output))
|
||||
|
||||
def assert_file_matches(original):
|
||||
expected = original.split('\n')
|
||||
# Remove the last item which will look like a duplicated
|
||||
# file ending newline
|
||||
expected.pop()
|
||||
diff = '\n'.join(goofy_diff(expected, actual))
|
||||
if diff:
|
||||
fail('Error: output files differ for %s:\n%s'
|
||||
% (filename, diff))
|
||||
|
||||
def order_json(json_obj):
|
||||
"""Sort the json object so that it can be compared properly."""
|
||||
if isinstance(json_obj, list):
|
||||
return sorted(order_json(elem) for elem in json_obj)
|
||||
if isinstance(json_obj, dict):
|
||||
return sorted(
|
||||
(key, order_json(value))
|
||||
for key, value in json_obj.items())
|
||||
else:
|
||||
return json_obj
|
||||
|
||||
def assert_json_matches(original):
|
||||
try:
|
||||
expected_json = json.loads(original)
|
||||
actual_json = json.loads(output)
|
||||
except ValueError:
|
||||
fail('Invalid json!\nExpected: %s\nActual: %s'
|
||||
% (original, output))
|
||||
|
||||
if order_json(expected_json) != order_json(actual_json):
|
||||
# Re-Use the same failure output if the json is different
|
||||
assert_file_matches(original)
|
||||
|
||||
if not os.environ.get('TESTS_FIX_EXAMPLES'):
|
||||
assert_output_matches()
|
||||
elif not empty:
|
||||
write_actual_file()
|
||||
|
||||
|
||||
# This method is mixed into the client class.
|
||||
# It requires the following fields: snippet_writer, content_type, and
|
||||
# "name," the last of which must be set before each call.
|
||||
def write_to_snippet(self, args, kwargs, resp, body):
|
||||
if self.name is None:
|
||||
raise RuntimeError("'name' not set before call.")
|
||||
url = args[0]
|
||||
method = args[1]
|
||||
request_headers = kwargs['headers']
|
||||
request_body = kwargs.get('body', None)
|
||||
response_headers = resp
|
||||
response_body = body
|
||||
|
||||
# Log request
|
||||
user_details = {
|
||||
'api_url': self.service_url,
|
||||
'service_url': self.service_url,
|
||||
'tenant': self.tenant,
|
||||
}
|
||||
self.snippet_writer.write_request_file(user_details, self.name,
|
||||
self.content_type, url, method,
|
||||
request_headers, request_body)
|
||||
self.snippet_writer.write_response_file(user_details, self.name,
|
||||
self.content_type, url, method,
|
||||
response_headers, response_body)
|
||||
|
||||
# Create a short url to assert against.
|
||||
short_url = url
|
||||
base_url = self.service_url
|
||||
for prefix in (base_url):
|
||||
if short_url.startswith(prefix):
|
||||
short_url = short_url[len(prefix):]
|
||||
self.old_info = {
|
||||
'url': shorten_url(short_url),
|
||||
'method': method,
|
||||
'request_headers': request_headers,
|
||||
'request_body': request_body,
|
||||
'response_headers': response_headers,
|
||||
'response_body': response_body
|
||||
}
|
||||
|
||||
|
||||
def add_fake_response_headers(headers):
|
||||
"""
|
||||
Fakes other items that would appear if you were using, just to make up
|
||||
an example, a proxy.
|
||||
"""
|
||||
conf = CONFIG.examples
|
||||
if 'via' in conf and 'via' not in headers:
|
||||
headers['via'] = conf['via']
|
||||
if 'server' in conf and 'server' not in headers:
|
||||
headers['server'] = conf['server']
|
||||
if 'date' not in headers:
|
||||
date_string = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
|
||||
headers['date'] = date_string
|
||||
|
||||
|
||||
class JsonClient(TroveHTTPClient):
|
||||
|
||||
content_type = 'json'
|
||||
|
||||
def http_log(self, args, kwargs, resp, body):
|
||||
add_fake_response_headers(resp)
|
||||
self.pretty_log(args, kwargs, resp, body)
|
||||
|
||||
def write_snippet():
|
||||
return write_to_snippet(self, args, kwargs, resp, body.decode())
|
||||
|
||||
self.write_snippet = write_snippet
|
File diff suppressed because it is too large
Load Diff
@ -183,6 +183,7 @@ class InstanceTestInfo(object):
|
||||
self.helper_user = None # Test helper user if exists.
|
||||
self.helper_database = None # Test helper database if exists.
|
||||
self.admin_user = None
|
||||
self.flavors = None
|
||||
|
||||
|
||||
class LogOnFail(type):
|
||||
@ -368,6 +369,8 @@ class TestRunner(object):
|
||||
inst_ids = [self.instance_info.id]
|
||||
self.register_debug_inst_ids(inst_ids)
|
||||
|
||||
self.instance_info.flavors = self.nova_client.flavors.list()
|
||||
|
||||
@classmethod
|
||||
def fail(cls, message):
|
||||
asserts.fail(message)
|
||||
@ -817,12 +820,14 @@ class TestRunner(object):
|
||||
return {"flavorRef": flavor_id, "volume": {"size": volume_size}}
|
||||
|
||||
def get_flavor(self, flavor_name):
|
||||
flavors = self.auth_client.find_flavors_by_name(flavor_name)
|
||||
self.assert_equal(
|
||||
1, len(flavors),
|
||||
"Unexpected number of flavors with name '%s' found." % flavor_name)
|
||||
flavor = None
|
||||
for item in self.instance_info.flavors:
|
||||
if item.name == flavor_name:
|
||||
flavor = item
|
||||
|
||||
return flavors[0]
|
||||
asserts.assert_is_not_none(flavor)
|
||||
|
||||
return flavor
|
||||
|
||||
def get_instance_flavor(self, fault_num=None):
|
||||
name_format = 'instance%s%s_flavor_name'
|
||||
@ -841,7 +846,7 @@ class TestRunner(object):
|
||||
return self.get_flavor(flavor_name)
|
||||
|
||||
def get_flavor_href(self, flavor):
|
||||
return self.auth_client.find_flavor_self_href(flavor)
|
||||
return flavor.id
|
||||
|
||||
def copy_dict(self, d, ignored_keys=None):
|
||||
return {k: v for k, v in d.items()
|
||||
@ -980,11 +985,10 @@ class CheckInstance(AttrCheck):
|
||||
if 'flavor' not in self.instance:
|
||||
self.fail("'flavor' not found in instance.")
|
||||
else:
|
||||
allowed_attrs = ['id', 'links']
|
||||
allowed_attrs = ['id']
|
||||
self.contains_allowed_attrs(
|
||||
self.instance['flavor'], allowed_attrs,
|
||||
msg="Flavor")
|
||||
self.links(self.instance['flavor']['links'])
|
||||
|
||||
def datastore(self):
|
||||
if 'datastore' not in self.instance:
|
||||
|
@ -140,7 +140,6 @@ class ClusterInstanceDetailViewTest(trove_testtools.TestCase):
|
||||
super(ClusterInstanceDetailViewTest, self).tearDown()
|
||||
|
||||
@patch.object(ClusterInstanceDetailView, '_build_links')
|
||||
@patch.object(ClusterInstanceDetailView, '_build_flavor_links')
|
||||
@patch.object(ClusterInstanceDetailView, '_build_configuration_info')
|
||||
def test_data(self, *args):
|
||||
view = ClusterInstanceDetailView(self.instance, self.req)
|
||||
@ -154,7 +153,6 @@ class ClusterInstanceDetailViewTest(trove_testtools.TestCase):
|
||||
self.assertNotIn('ip', result['instance'])
|
||||
|
||||
@patch.object(ClusterInstanceDetailView, '_build_links')
|
||||
@patch.object(ClusterInstanceDetailView, '_build_flavor_links')
|
||||
@patch.object(ClusterInstanceDetailView, '_build_configuration_info')
|
||||
def test_data_ip(self, *args):
|
||||
self.instance.hostname = None
|
||||
|
@ -41,10 +41,8 @@ class InstanceDetailViewTest(trove_testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(InstanceDetailViewTest, self).setUp()
|
||||
self.build_links_method = InstanceView._build_links
|
||||
self.build_flavor_links_method = InstanceView._build_flavor_links
|
||||
self.build_config_method = InstanceDetailView._build_configuration_info
|
||||
InstanceView._build_links = Mock()
|
||||
InstanceView._build_flavor_links = Mock()
|
||||
InstanceDetailView._build_configuration_info = Mock()
|
||||
self.instance = Mock()
|
||||
self.instance.created = 'Yesterday'
|
||||
@ -79,7 +77,6 @@ class InstanceDetailViewTest(trove_testtools.TestCase):
|
||||
def tearDown(self):
|
||||
super(InstanceDetailViewTest, self).tearDown()
|
||||
InstanceView._build_links = self.build_links_method
|
||||
InstanceView._build_flavor_links = self.build_flavor_links_method
|
||||
InstanceDetailView._build_configuration_info = self.build_config_method
|
||||
|
||||
def test_data_hostname(self):
|
||||
|
@ -67,37 +67,6 @@ class TestClient(object):
|
||||
resp, body = self.real_client.client.last_response
|
||||
return resp.status
|
||||
|
||||
@staticmethod
|
||||
def find_flavor_self_href(flavor):
|
||||
self_links = [link for link in flavor.links if link['rel'] == 'self']
|
||||
asserts.assert_true(len(self_links) > 0, "Flavor had no self href!")
|
||||
flavor_href = self_links[0]['href']
|
||||
asserts.assert_false(flavor_href is None,
|
||||
"Flavor link self href missing.")
|
||||
return flavor_href
|
||||
|
||||
def find_flavors_by(self, condition, flavor_manager=None):
|
||||
flavor_manager = flavor_manager or self.flavors
|
||||
flavors = flavor_manager.list()
|
||||
return [flavor for flavor in flavors if condition(flavor)]
|
||||
|
||||
def find_flavors_by_name(self, name, flavor_manager=None):
|
||||
return self.find_flavors_by(lambda flavor: flavor.name == name,
|
||||
flavor_manager)
|
||||
|
||||
def find_flavors_by_ram(self, ram, flavor_manager=None):
|
||||
return self.find_flavors_by(lambda flavor: flavor.ram == ram,
|
||||
flavor_manager)
|
||||
|
||||
def find_flavor_and_self_href(self, flavor_id, flavor_manager=None):
|
||||
"""Given an ID, returns flavor and its self href."""
|
||||
flavor_manager = flavor_manager or self.flavors
|
||||
asserts.assert_false(flavor_id is None)
|
||||
flavor = flavor_manager.get(flavor_id)
|
||||
asserts.assert_false(flavor is None)
|
||||
flavor_href = self.find_flavor_self_href(flavor)
|
||||
return flavor, flavor_href
|
||||
|
||||
def __getattr__(self, item):
|
||||
if item == "__setstate__":
|
||||
raise AttributeError(item)
|
||||
|
Loading…
Reference in New Issue
Block a user