Merge "Remove the heat tests"
This commit is contained in:
commit
72d54b6877
@ -0,0 +1,4 @@
|
||||
---
|
||||
upgrade:
|
||||
- The Heat API tests have been removed from tempest, they were unmaintained.
|
||||
The future direction of api test for heat is their in-tree Gabbi tests
|
@ -1,169 +0,0 @@
|
||||
# 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 os.path
|
||||
|
||||
import yaml
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib.common.utils import test_utils
|
||||
import tempest.test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseOrchestrationTest(tempest.test.BaseTestCase):
|
||||
"""Base test case class for all Orchestration API tests."""
|
||||
|
||||
credentials = ['primary']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseOrchestrationTest, cls).skip_checks()
|
||||
if not CONF.service_available.heat:
|
||||
raise cls.skipException("Heat support is required")
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
super(BaseOrchestrationTest, cls).setup_credentials()
|
||||
stack_owner_role = CONF.orchestration.stack_owner_role
|
||||
cls.os = cls.get_client_manager(roles=[stack_owner_role])
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(BaseOrchestrationTest, cls).setup_clients()
|
||||
cls.orchestration_client = cls.os_primary.orchestration_client
|
||||
cls.client = cls.orchestration_client
|
||||
cls.servers_client = cls.os_primary.servers_client
|
||||
cls.keypairs_client = cls.os_primary.keypairs_client
|
||||
cls.networks_client = cls.os_primary.networks_client
|
||||
cls.images_v2_client = cls.os_primary.image_client_v2
|
||||
cls.volumes_client = cls.os_primary.volumes_v2_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseOrchestrationTest, cls).resource_setup()
|
||||
cls.build_timeout = CONF.orchestration.build_timeout
|
||||
cls.build_interval = CONF.orchestration.build_interval
|
||||
cls.stacks = []
|
||||
cls.keypairs = []
|
||||
cls.images = []
|
||||
|
||||
@classmethod
|
||||
def create_stack(cls, stack_name, template_data, parameters=None,
|
||||
environment=None, files=None):
|
||||
if parameters is None:
|
||||
parameters = {}
|
||||
body = cls.client.create_stack(
|
||||
stack_name,
|
||||
template=template_data,
|
||||
parameters=parameters,
|
||||
environment=environment,
|
||||
files=files)
|
||||
stack_id = body.response['location'].split('/')[-1]
|
||||
stack_identifier = '%s/%s' % (stack_name, stack_id)
|
||||
cls.stacks.append(stack_identifier)
|
||||
return stack_identifier
|
||||
|
||||
@classmethod
|
||||
def _clear_stacks(cls):
|
||||
for stack_identifier in cls.stacks:
|
||||
test_utils.call_and_ignore_notfound_exc(
|
||||
cls.client.delete_stack, stack_identifier)
|
||||
|
||||
for stack_identifier in cls.stacks:
|
||||
test_utils.call_and_ignore_notfound_exc(
|
||||
cls.client.wait_for_stack_status, stack_identifier,
|
||||
'DELETE_COMPLETE')
|
||||
|
||||
@classmethod
|
||||
def _create_keypair(cls, name_start='keypair-heat-'):
|
||||
kp_name = data_utils.rand_name(name_start)
|
||||
body = cls.keypairs_client.create_keypair(name=kp_name)['keypair']
|
||||
cls.keypairs.append(kp_name)
|
||||
return body
|
||||
|
||||
@classmethod
|
||||
def _clear_keypairs(cls):
|
||||
for kp_name in cls.keypairs:
|
||||
try:
|
||||
cls.keypairs_client.delete_keypair(kp_name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _create_image(cls, name_start='image-heat-', container_format='bare',
|
||||
disk_format='iso'):
|
||||
image_name = data_utils.rand_name(name_start)
|
||||
body = cls.images_v2_client.create_image(image_name,
|
||||
container_format,
|
||||
disk_format)
|
||||
image_id = body['id']
|
||||
cls.images.append(image_id)
|
||||
return body
|
||||
|
||||
@classmethod
|
||||
def _clear_images(cls):
|
||||
for image_id in cls.images:
|
||||
test_utils.call_and_ignore_notfound_exc(
|
||||
cls.images_v2_client.delete_image, image_id)
|
||||
|
||||
@classmethod
|
||||
def read_template(cls, name, ext='yaml'):
|
||||
loc = ["stacks", "templates", "%s.%s" % (name, ext)]
|
||||
fullpath = os.path.join(os.path.dirname(__file__), *loc)
|
||||
|
||||
with open(fullpath, "r") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
@classmethod
|
||||
def load_template(cls, name, ext='yaml'):
|
||||
loc = ["stacks", "templates", "%s.%s" % (name, ext)]
|
||||
fullpath = os.path.join(os.path.dirname(__file__), *loc)
|
||||
|
||||
with open(fullpath, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
cls._clear_stacks()
|
||||
cls._clear_keypairs()
|
||||
cls._clear_images()
|
||||
super(BaseOrchestrationTest, cls).resource_cleanup()
|
||||
|
||||
@staticmethod
|
||||
def stack_output(stack, output_key):
|
||||
"""Return a stack output value for a given key."""
|
||||
return next((o['output_value'] for o in stack['outputs']
|
||||
if o['output_key'] == output_key), None)
|
||||
|
||||
def assert_fields_in_dict(self, obj, *fields):
|
||||
for field in fields:
|
||||
self.assertIn(field, obj)
|
||||
|
||||
def list_resources(self, stack_identifier):
|
||||
"""Get a dict mapping of resource names to types."""
|
||||
resources = self.client.list_resources(stack_identifier)['resources']
|
||||
self.assertIsInstance(resources, list)
|
||||
for res in resources:
|
||||
self.assert_fields_in_dict(res, 'logical_resource_id',
|
||||
'resource_type', 'resource_status',
|
||||
'updated_time')
|
||||
|
||||
return dict((r['resource_name'], r['resource_type'])
|
||||
for r in resources)
|
||||
|
||||
def get_stack_output(self, stack_identifier, output_key):
|
||||
body = self.client.show_stack(stack_identifier)['stack']
|
||||
return self.stack_output(body, output_key)
|
@ -1,33 +0,0 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
parameters:
|
||||
volume_size:
|
||||
type: number
|
||||
default: 1
|
||||
|
||||
resources:
|
||||
volume:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
size: { get_param: volume_size }
|
||||
description: a descriptive description
|
||||
name: volume_name
|
||||
|
||||
outputs:
|
||||
status:
|
||||
description: status
|
||||
value: { get_attr: ['volume', 'status'] }
|
||||
|
||||
size:
|
||||
description: size
|
||||
value: { get_attr: ['volume', 'size'] }
|
||||
|
||||
display_description:
|
||||
description: display_description
|
||||
value: { get_attr: ['volume', 'display_description'] }
|
||||
|
||||
display_name:
|
||||
value: { get_attr: ['volume', 'display_name'] }
|
||||
|
||||
volume_id:
|
||||
value: { get_resource: volume }
|
@ -1,34 +0,0 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
parameters:
|
||||
volume_size:
|
||||
type: number
|
||||
default: 1
|
||||
|
||||
resources:
|
||||
volume:
|
||||
deletion_policy: 'Retain'
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
size: { get_param: volume_size }
|
||||
description: a descriptive description
|
||||
name: volume_name
|
||||
|
||||
outputs:
|
||||
status:
|
||||
description: status
|
||||
value: { get_attr: ['volume', 'status'] }
|
||||
|
||||
size:
|
||||
description: size
|
||||
value: { get_attr: ['volume', 'size'] }
|
||||
|
||||
display_description:
|
||||
description: display_description
|
||||
value: { get_attr: ['volume', 'display_description'] }
|
||||
|
||||
display_name:
|
||||
value: { get_attr: ['volume', 'display_name'] }
|
||||
|
||||
volume_id:
|
||||
value: { get_resource: volume }
|
@ -1,72 +0,0 @@
|
||||
heat_template_version: '2013-05-23'
|
||||
description: |
|
||||
Template which creates single EC2 instance
|
||||
parameters:
|
||||
KeyName:
|
||||
type: string
|
||||
InstanceType:
|
||||
type: string
|
||||
ImageId:
|
||||
type: string
|
||||
SubNetCidr:
|
||||
type: string
|
||||
ExternalNetworkId:
|
||||
type: string
|
||||
DNSServers:
|
||||
type: comma_delimited_list
|
||||
timeout:
|
||||
type: number
|
||||
resources:
|
||||
Network:
|
||||
type: OS::Neutron::Net
|
||||
properties:
|
||||
name: NewNetwork
|
||||
Subnet:
|
||||
type: OS::Neutron::Subnet
|
||||
properties:
|
||||
network_id: {Ref: Network}
|
||||
name: NewSubnet
|
||||
ip_version: 4
|
||||
cidr: { get_param: SubNetCidr }
|
||||
dns_nameservers: { get_param: DNSServers }
|
||||
Router:
|
||||
type: OS::Neutron::Router
|
||||
properties:
|
||||
name: NewRouter
|
||||
admin_state_up: true
|
||||
external_gateway_info:
|
||||
network: {get_param: ExternalNetworkId}
|
||||
RouterInterface:
|
||||
type: OS::Neutron::RouterInterface
|
||||
properties:
|
||||
router_id: {get_resource: Router}
|
||||
subnet_id: {get_resource: Subnet}
|
||||
Server:
|
||||
type: OS::Nova::Server
|
||||
metadata:
|
||||
Name: SmokeServerNeutron
|
||||
properties:
|
||||
image: {get_param: ImageId}
|
||||
flavor: {get_param: InstanceType}
|
||||
key_name: {get_param: KeyName}
|
||||
networks:
|
||||
- network: {get_resource: Network}
|
||||
user_data_format: RAW
|
||||
user_data:
|
||||
str_replace:
|
||||
template: |
|
||||
#!/bin/sh -v
|
||||
|
||||
SIGNAL_DATA='{"Status": "SUCCESS", "Reason": "SmokeServerNeutron created", "Data": "Application has completed configuration.", "UniqueId": "00000"}'
|
||||
while ! curl --insecure --fail -X PUT -H 'Content-Type:' --data-binary "$SIGNAL_DATA" \
|
||||
'wait_handle' ; do sleep 3; done
|
||||
params:
|
||||
wait_handle: {get_resource: WaitHandleNeutron}
|
||||
WaitHandleNeutron:
|
||||
type: AWS::CloudFormation::WaitConditionHandle
|
||||
WaitCondition:
|
||||
type: AWS::CloudFormation::WaitCondition
|
||||
depends_on: Server
|
||||
properties:
|
||||
Handle: {get_resource: WaitHandleNeutron}
|
||||
Timeout: {get_param: timeout}
|
@ -1,36 +0,0 @@
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: |
|
||||
Template which creates some simple resources
|
||||
Parameters:
|
||||
trigger:
|
||||
Type: String
|
||||
Default: not_yet
|
||||
image:
|
||||
Type: String
|
||||
flavor:
|
||||
Type: String
|
||||
Resources:
|
||||
fluffy:
|
||||
Type: AWS::AutoScaling::LaunchConfiguration
|
||||
Metadata:
|
||||
kittens:
|
||||
- Tom
|
||||
- Stinky
|
||||
Properties:
|
||||
ImageId: {Ref: image}
|
||||
InstanceType: {Ref: flavor}
|
||||
UserData:
|
||||
Fn::Replace:
|
||||
- variable_a: {Ref: trigger}
|
||||
variable_b: bee
|
||||
- |
|
||||
A == variable_a
|
||||
B == variable_b
|
||||
Outputs:
|
||||
fluffy:
|
||||
Description: "fluffies irc nick"
|
||||
Value:
|
||||
Fn::Replace:
|
||||
- nick: {Ref: fluffy}
|
||||
- |
|
||||
#nick
|
@ -1,48 +0,0 @@
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Template which create two key pairs.",
|
||||
"Parameters" : {
|
||||
"KeyPairName1": {
|
||||
"Type": "String",
|
||||
"Default": "testkey1"
|
||||
},
|
||||
"KeyPairName2": {
|
||||
"Type": "String",
|
||||
"Default": "testkey2"
|
||||
}
|
||||
},
|
||||
"Resources" : {
|
||||
"KeyPairSavePrivate": {
|
||||
"Type": "OS::Nova::KeyPair",
|
||||
"Properties": {
|
||||
"name" : { "Ref" : "KeyPairName1" },
|
||||
"save_private_key": true
|
||||
}
|
||||
},
|
||||
"KeyPairDontSavePrivate": {
|
||||
"Type": "OS::Nova::KeyPair",
|
||||
"Properties": {
|
||||
"name" : { "Ref" : "KeyPairName2" },
|
||||
"save_private_key": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"KeyPair_PublicKey": {
|
||||
"Description": "Public Key of generated keypair.",
|
||||
"Value": { "Fn::GetAtt" : ["KeyPairSavePrivate", "public_key"] }
|
||||
},
|
||||
"KeyPair_PrivateKey": {
|
||||
"Description": "Private Key of generated keypair.",
|
||||
"Value": { "Fn::GetAtt" : ["KeyPairSavePrivate", "private_key"] }
|
||||
},
|
||||
"KeyPairDontSavePrivate_PublicKey": {
|
||||
"Description": "Public Key of generated keypair.",
|
||||
"Value": { "Fn::GetAtt" : ["KeyPairDontSavePrivate", "public_key"] }
|
||||
},
|
||||
"KeyPairDontSavePrivate_PrivateKey": {
|
||||
"Description": "Private Key of generated keypair.",
|
||||
"Value": { "Fn::GetAtt" : ["KeyPairDontSavePrivate", "private_key"] }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
Template which creates two key pairs.
|
||||
|
||||
parameters:
|
||||
KeyPairName1:
|
||||
type: string
|
||||
default: testkey
|
||||
|
||||
KeyPairName2:
|
||||
type: string
|
||||
default: testkey2
|
||||
|
||||
resources:
|
||||
KeyPairSavePrivate:
|
||||
type: OS::Nova::KeyPair
|
||||
properties:
|
||||
name: { get_param: KeyPairName1 }
|
||||
save_private_key: true
|
||||
|
||||
KeyPairDontSavePrivate:
|
||||
type: OS::Nova::KeyPair
|
||||
properties:
|
||||
name: { get_param: KeyPairName2 }
|
||||
save_private_key: false
|
||||
|
||||
outputs:
|
||||
KeyPair_PublicKey:
|
||||
description: Public Key of generated keypair
|
||||
value: { get_attr: [KeyPairSavePrivate, public_key] }
|
||||
|
||||
KeyPair_PrivateKey:
|
||||
description: Private Key of generated keypair
|
||||
value: { get_attr: [KeyPairSavePrivate, private_key] }
|
||||
|
||||
KeyPairDontSavePrivate_PublicKey:
|
||||
description: Public Key of generated keypair
|
||||
value: { get_attr: [KeyPairDontSavePrivate, public_key] }
|
||||
|
||||
KeyPairDontSavePrivate_PrivateKey:
|
||||
description: Private Key of generated keypair
|
||||
value: { get_attr: [KeyPairDontSavePrivate, private_key] }
|
@ -1,18 +0,0 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
parameters:
|
||||
random_length:
|
||||
type: number
|
||||
default: 10
|
||||
|
||||
resources:
|
||||
random:
|
||||
type: OS::Heat::RandomString
|
||||
properties:
|
||||
length: {get_param: random_length}
|
||||
|
||||
outputs:
|
||||
random_length:
|
||||
value: {get_param: random_length}
|
||||
random_value:
|
||||
value: {get_attr: [random, value]}
|
@ -1,23 +0,0 @@
|
||||
heat_template_version: 2013-05-23
|
||||
description: Template which creates a Swift container resource
|
||||
|
||||
resources:
|
||||
SwiftContainerWebsite:
|
||||
deletion_policy: "Delete"
|
||||
type: OS::Swift::Container
|
||||
properties:
|
||||
X-Container-Read: ".r:*"
|
||||
X-Container-Meta:
|
||||
web-index: "index.html"
|
||||
web-error: "error.html"
|
||||
|
||||
SwiftContainer:
|
||||
type: OS::Swift::Container
|
||||
|
||||
outputs:
|
||||
WebsiteURL:
|
||||
description: "URL for website hosted on S3"
|
||||
value: { get_attr: [SwiftContainer, WebsiteURL] }
|
||||
DomainName:
|
||||
description: "Domain of Swift host"
|
||||
value: { get_attr: [SwiftContainer, DomainName] }
|
@ -1,92 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class StackEnvironmentTest(base.BaseOrchestrationTest):
|
||||
|
||||
@decorators.idempotent_id('37d4346b-1abd-4442-b7b1-2a4e5749a1e3')
|
||||
def test_environment_parameter(self):
|
||||
"""Test passing a stack parameter via the environment."""
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
template = self.read_template('random_string')
|
||||
environment = {'parameters': {'random_length': 20}}
|
||||
|
||||
stack_identifier = self.create_stack(stack_name, template,
|
||||
environment=environment)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
random_len = self.get_stack_output(stack_identifier, 'random_length')
|
||||
self.assertEqual(20, random_len)
|
||||
|
||||
random_value = self.get_stack_output(stack_identifier, 'random_value')
|
||||
self.assertEqual(20, len(random_value))
|
||||
|
||||
@decorators.idempotent_id('73bce717-ad22-4853-bbef-6ed89b632701')
|
||||
def test_environment_provider_resource(self):
|
||||
"""Test passing resource_registry defining a provider resource."""
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
random:
|
||||
type: My:Random::String
|
||||
outputs:
|
||||
random_value:
|
||||
value: {get_attr: [random, random_value]}
|
||||
'''
|
||||
environment = {'resource_registry':
|
||||
{'My:Random::String': 'my_random.yaml'}}
|
||||
files = {'my_random.yaml': self.read_template('random_string')}
|
||||
|
||||
stack_identifier = self.create_stack(stack_name, template,
|
||||
environment=environment,
|
||||
files=files)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
# random_string.yaml specifies a length of 10
|
||||
random_value = self.get_stack_output(stack_identifier, 'random_value')
|
||||
random_string_template = self.load_template('random_string')
|
||||
expected_length = random_string_template['parameters'][
|
||||
'random_length']['default']
|
||||
self.assertEqual(expected_length, len(random_value))
|
||||
|
||||
@decorators.idempotent_id('9d682e5a-f4bb-47d5-8472-9d3cacb855df')
|
||||
def test_files_provider_resource(self):
|
||||
"""Test untyped defining of a provider resource via "files"."""
|
||||
# It's also possible to specify the filename directly in the template.
|
||||
# without adding the type alias to resource_registry
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
template = '''
|
||||
heat_template_version: 2013-05-23
|
||||
resources:
|
||||
random:
|
||||
type: my_random.yaml
|
||||
outputs:
|
||||
random_value:
|
||||
value: {get_attr: [random, random_value]}
|
||||
'''
|
||||
files = {'my_random.yaml': self.read_template('random_string')}
|
||||
|
||||
stack_identifier = self.create_stack(stack_name, template,
|
||||
files=files)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
# random_string.yaml specifies a length of 10
|
||||
random_value = self.get_stack_output(stack_identifier, 'random_value')
|
||||
random_string_template = self.load_template('random_string')
|
||||
expected_length = random_string_template['parameters'][
|
||||
'random_length']['default']
|
||||
self.assertEqual(expected_length, len(random_value))
|
@ -1,49 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class TestServerStackLimits(base.BaseOrchestrationTest):
|
||||
|
||||
@decorators.idempotent_id('ec9bed71-c460-45c9-ab98-295caa9fd76b')
|
||||
def test_exceed_max_template_size_fails(self):
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
fill = 'A' * CONF.orchestration.max_template_size
|
||||
template = '''
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: '%s'
|
||||
Outputs:
|
||||
Foo: bar''' % fill
|
||||
ex = self.assertRaises(lib_exc.BadRequest, self.create_stack,
|
||||
stack_name, template)
|
||||
self.assertIn('exceeds maximum allowed size', str(ex))
|
||||
|
||||
@decorators.idempotent_id('d1b83e73-7cad-4a22-9839-036548c5387c')
|
||||
def test_exceed_max_resources_per_stack(self):
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
# Create a big template, one resource more than the limit
|
||||
template = 'heat_template_version: \'2013-05-23\'\nresources:\n'
|
||||
rsrc_snippet = ' random%s:\n type: \'OS::Heat::RandomString\'\n'
|
||||
num_resources = CONF.orchestration.max_resources_per_stack + 1
|
||||
for i in range(num_resources):
|
||||
template += rsrc_snippet % i
|
||||
|
||||
ex = self.assertRaises(lib_exc.BadRequest, self.create_stack,
|
||||
stack_name, template)
|
||||
self.assertIn('Maximum resources per stack exceeded', str(ex))
|
@ -1,196 +0,0 @@
|
||||
# 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 netaddr
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tempest.api.orchestration import base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
from tempest import test
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NeutronResourcesTestJSON(base.BaseOrchestrationTest):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(NeutronResourcesTestJSON, cls).skip_checks()
|
||||
if not CONF.service_available.neutron:
|
||||
raise cls.skipException("Neutron support is required")
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
cls.set_network_resources()
|
||||
super(NeutronResourcesTestJSON, cls).setup_credentials()
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(NeutronResourcesTestJSON, cls).setup_clients()
|
||||
cls.subnets_client = cls.os_primary.subnets_client
|
||||
cls.ports_client = cls.os_primary.ports_client
|
||||
cls.routers_client = cls.os_primary.routers_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(NeutronResourcesTestJSON, cls).resource_setup()
|
||||
cls.neutron_basic_template = cls.load_template('neutron_basic')
|
||||
cls.stack_name = data_utils.rand_name('heat')
|
||||
template = cls.read_template('neutron_basic')
|
||||
cls.keypair_name = (CONF.orchestration.keypair_name or
|
||||
cls._create_keypair()['name'])
|
||||
cls.external_network_id = CONF.network.public_network_id
|
||||
|
||||
tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr)
|
||||
mask_bits = CONF.network.project_network_mask_bits
|
||||
cls.subnet_cidr = tenant_cidr.subnet(mask_bits).next()
|
||||
|
||||
# create the stack
|
||||
cls.stack_identifier = cls.create_stack(
|
||||
cls.stack_name,
|
||||
template,
|
||||
parameters={
|
||||
'KeyName': cls.keypair_name,
|
||||
'InstanceType': CONF.orchestration.instance_type,
|
||||
'ImageId': CONF.compute.image_ref,
|
||||
'ExternalNetworkId': cls.external_network_id,
|
||||
'timeout': CONF.orchestration.build_timeout,
|
||||
'DNSServers': CONF.network.dns_servers,
|
||||
'SubNetCidr': str(cls.subnet_cidr)
|
||||
})
|
||||
cls.stack_id = cls.stack_identifier.split('/')[1]
|
||||
try:
|
||||
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
|
||||
resources = (cls.client.list_resources(cls.stack_identifier)
|
||||
['resources'])
|
||||
except exceptions.TimeoutException:
|
||||
if CONF.compute_feature_enabled.console_output:
|
||||
# attempt to log the server console to help with debugging
|
||||
# the cause of the server not signalling the waitcondition
|
||||
# to heat.
|
||||
body = cls.client.show_resource(cls.stack_identifier,
|
||||
'Server')
|
||||
server_id = body.get('physical_resource_id')
|
||||
if server_id:
|
||||
LOG.debug('Console output for %s', server_id)
|
||||
output = cls.servers_client.get_console_output(
|
||||
server_id)['output']
|
||||
LOG.debug(output)
|
||||
else:
|
||||
LOG.debug('Server resource is %s', body)
|
||||
raise # original exception
|
||||
|
||||
cls.test_resources = {}
|
||||
for resource in resources:
|
||||
cls.test_resources[resource['logical_resource_id']] = resource
|
||||
|
||||
@decorators.idempotent_id('f9e2664c-bc44-4eef-98b6-495e4f9d74b3')
|
||||
def test_created_resources(self):
|
||||
"""Verifies created neutron resources."""
|
||||
resources = [('Network', self.neutron_basic_template['resources'][
|
||||
'Network']['type']),
|
||||
('Subnet', self.neutron_basic_template['resources'][
|
||||
'Subnet']['type']),
|
||||
('RouterInterface', self.neutron_basic_template[
|
||||
'resources']['RouterInterface']['type']),
|
||||
('Server', self.neutron_basic_template['resources'][
|
||||
'Server']['type'])]
|
||||
for resource_name, resource_type in resources:
|
||||
resource = self.test_resources.get(resource_name, None)
|
||||
self.assertIsInstance(resource, dict)
|
||||
self.assertEqual(resource_name, resource['logical_resource_id'])
|
||||
self.assertEqual(resource_type, resource['resource_type'])
|
||||
self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
|
||||
|
||||
@decorators.idempotent_id('c572b915-edb1-4e90-b196-c7199a6848c0')
|
||||
@test.services('network')
|
||||
def test_created_network(self):
|
||||
"""Verifies created network."""
|
||||
network_id = self.test_resources.get('Network')['physical_resource_id']
|
||||
body = self.networks_client.show_network(network_id)
|
||||
network = body['network']
|
||||
self.assertIsInstance(network, dict)
|
||||
self.assertEqual(network_id, network['id'])
|
||||
self.assertEqual(self.neutron_basic_template['resources'][
|
||||
'Network']['properties']['name'], network['name'])
|
||||
|
||||
@decorators.idempotent_id('e8f84b96-f9d7-4684-ad5f-340203e9f2c2')
|
||||
@test.services('network')
|
||||
def test_created_subnet(self):
|
||||
"""Verifies created subnet."""
|
||||
subnet_id = self.test_resources.get('Subnet')['physical_resource_id']
|
||||
body = self.subnets_client.show_subnet(subnet_id)
|
||||
subnet = body['subnet']
|
||||
network_id = self.test_resources.get('Network')['physical_resource_id']
|
||||
self.assertEqual(subnet_id, subnet['id'])
|
||||
self.assertEqual(network_id, subnet['network_id'])
|
||||
self.assertEqual(self.neutron_basic_template['resources'][
|
||||
'Subnet']['properties']['name'], subnet['name'])
|
||||
self.assertEqual(sorted(CONF.network.dns_servers),
|
||||
sorted(subnet['dns_nameservers']))
|
||||
self.assertEqual(self.neutron_basic_template['resources'][
|
||||
'Subnet']['properties']['ip_version'], subnet['ip_version'])
|
||||
self.assertEqual(str(self.subnet_cidr), subnet['cidr'])
|
||||
|
||||
@decorators.idempotent_id('96af4c7f-5069-44bc-bdcf-c0390f8a67d1')
|
||||
@test.services('network')
|
||||
def test_created_router(self):
|
||||
"""Verifies created router."""
|
||||
router_id = self.test_resources.get('Router')['physical_resource_id']
|
||||
body = self.routers_client.show_router(router_id)
|
||||
router = body['router']
|
||||
self.assertEqual(self.neutron_basic_template['resources'][
|
||||
'Router']['properties']['name'], router['name'])
|
||||
self.assertEqual(self.external_network_id,
|
||||
router['external_gateway_info']['network_id'])
|
||||
self.assertEqual(True, router['admin_state_up'])
|
||||
|
||||
@decorators.idempotent_id('89f605bd-153e-43ee-a0ed-9919b63423c5')
|
||||
@test.services('network')
|
||||
def test_created_router_interface(self):
|
||||
"""Verifies created router interface."""
|
||||
router_id = self.test_resources.get('Router')['physical_resource_id']
|
||||
network_id = self.test_resources.get('Network')['physical_resource_id']
|
||||
subnet_id = self.test_resources.get('Subnet')['physical_resource_id']
|
||||
body = self.ports_client.list_ports()
|
||||
ports = body['ports']
|
||||
router_ports = filter(lambda port: port['device_id'] ==
|
||||
router_id, ports)
|
||||
created_network_ports = filter(lambda port: port['network_id'] ==
|
||||
network_id, router_ports)
|
||||
self.assertEqual(1, len(created_network_ports))
|
||||
router_interface = created_network_ports[0]
|
||||
fixed_ips = router_interface['fixed_ips']
|
||||
subnet_fixed_ips = filter(lambda port: port['subnet_id'] ==
|
||||
subnet_id, fixed_ips)
|
||||
self.assertEqual(1, len(subnet_fixed_ips))
|
||||
router_interface_ip = subnet_fixed_ips[0]['ip_address']
|
||||
self.assertEqual(str(self.subnet_cidr.iter_hosts().next()),
|
||||
router_interface_ip)
|
||||
|
||||
@decorators.idempotent_id('75d85316-4ac2-4c0e-a1a9-edd2148fc10e')
|
||||
@test.services('compute', 'network')
|
||||
def test_created_server(self):
|
||||
"""Verifies created sever."""
|
||||
server_id = self.test_resources.get('Server')['physical_resource_id']
|
||||
server = self.servers_client.show_server(server_id)['server']
|
||||
self.assertEqual(self.keypair_name, server['key_name'])
|
||||
self.assertEqual('ACTIVE', server['status'])
|
||||
network = server['addresses'][self.neutron_basic_template['resources'][
|
||||
'Network']['properties']['name']][0]
|
||||
self.assertEqual(4, network['version'])
|
||||
self.assertIn(netaddr.IPAddress(network['addr']), self.subnet_cidr)
|
@ -1,150 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class StacksTestJSON(base.BaseOrchestrationTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(StacksTestJSON, cls).resource_setup()
|
||||
cls.stack_name = data_utils.rand_name('heat')
|
||||
template = cls.read_template('non_empty_stack')
|
||||
image_id = (CONF.compute.image_ref or
|
||||
cls._create_image()['id'])
|
||||
flavor = CONF.orchestration.instance_type
|
||||
# create the stack
|
||||
cls.stack_identifier = cls.create_stack(
|
||||
cls.stack_name,
|
||||
template,
|
||||
parameters={
|
||||
'trigger': 'start',
|
||||
'image': image_id,
|
||||
'flavor': flavor
|
||||
})
|
||||
cls.stack_id = cls.stack_identifier.split('/')[1]
|
||||
cls.resource_name = 'fluffy'
|
||||
cls.resource_type = 'AWS::AutoScaling::LaunchConfiguration'
|
||||
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
|
||||
|
||||
def _list_stacks(self, expected_num=None, **filter_kwargs):
|
||||
stacks = self.client.list_stacks(params=filter_kwargs)['stacks']
|
||||
self.assertIsInstance(stacks, list)
|
||||
if expected_num is not None:
|
||||
self.assertEqual(expected_num, len(stacks))
|
||||
return stacks
|
||||
|
||||
@decorators.idempotent_id('065c652a-720d-4760-9132-06aedeb8e3ab')
|
||||
def test_stack_list(self):
|
||||
"""Created stack should be in the list of existing stacks."""
|
||||
stacks = self._list_stacks()
|
||||
stacks_names = map(lambda stack: stack['stack_name'], stacks)
|
||||
self.assertIn(self.stack_name, stacks_names)
|
||||
|
||||
@decorators.idempotent_id('992f96e3-41ee-4ff6-91c7-bcfb670c0919')
|
||||
def test_stack_show(self):
|
||||
"""Getting details about created stack should be possible."""
|
||||
stack = self.client.show_stack(self.stack_name)['stack']
|
||||
self.assertIsInstance(stack, dict)
|
||||
self.assert_fields_in_dict(stack, 'stack_name', 'id', 'links',
|
||||
'parameters', 'outputs', 'disable_rollback',
|
||||
'stack_status_reason', 'stack_status',
|
||||
'creation_time', 'updated_time',
|
||||
'capabilities', 'notification_topics',
|
||||
'timeout_mins', 'template_description')
|
||||
self.assert_fields_in_dict(stack['parameters'], 'AWS::StackId',
|
||||
'trigger', 'AWS::Region', 'AWS::StackName')
|
||||
self.assertEqual(True, stack['disable_rollback'],
|
||||
'disable_rollback should default to True')
|
||||
self.assertEqual(self.stack_name, stack['stack_name'])
|
||||
self.assertEqual(self.stack_id, stack['id'])
|
||||
self.assertEqual('fluffy', stack['outputs'][0]['output_key'])
|
||||
|
||||
@decorators.idempotent_id('fe719f7a-305a-44d8-bbb5-c91e93d9da17')
|
||||
def test_suspend_resume_stack(self):
|
||||
"""Suspend and resume a stack."""
|
||||
self.client.suspend_stack(self.stack_identifier)
|
||||
self.client.wait_for_stack_status(self.stack_identifier,
|
||||
'SUSPEND_COMPLETE')
|
||||
self.client.resume_stack(self.stack_identifier)
|
||||
self.client.wait_for_stack_status(self.stack_identifier,
|
||||
'RESUME_COMPLETE')
|
||||
|
||||
@decorators.idempotent_id('c951d55e-7cce-4c1f-83a0-bad735437fa6')
|
||||
def test_list_resources(self):
|
||||
"""Get list of created resources for the stack should be possible."""
|
||||
resources = self.list_resources(self.stack_identifier)
|
||||
self.assertEqual({self.resource_name: self.resource_type}, resources)
|
||||
|
||||
@decorators.idempotent_id('2aba03b3-392f-4237-900b-1f5a5e9bd962')
|
||||
def test_show_resource(self):
|
||||
"""Getting details about created resource should be possible."""
|
||||
resource = self.client.show_resource(self.stack_identifier,
|
||||
self.resource_name)['resource']
|
||||
self.assertIsInstance(resource, dict)
|
||||
self.assert_fields_in_dict(resource, 'resource_name', 'description',
|
||||
'links', 'logical_resource_id',
|
||||
'resource_status', 'updated_time',
|
||||
'required_by', 'resource_status_reason',
|
||||
'physical_resource_id', 'resource_type')
|
||||
self.assertEqual(self.resource_name, resource['logical_resource_id'])
|
||||
self.assertEqual(self.resource_type, resource['resource_type'])
|
||||
|
||||
@decorators.idempotent_id('898070a9-eba5-4fae-b7d6-cf3ffa03090f')
|
||||
def test_resource_metadata(self):
|
||||
"""Getting metadata for created resources should be possible."""
|
||||
metadata = self.client.show_resource_metadata(
|
||||
self.stack_identifier,
|
||||
self.resource_name)['metadata']
|
||||
self.assertIsInstance(metadata, dict)
|
||||
self.assertEqual(['Tom', 'Stinky'], metadata.get('kittens', None))
|
||||
|
||||
@decorators.idempotent_id('46567533-0a7f-483b-8942-fa19e0f17839')
|
||||
def test_list_events(self):
|
||||
"""Getting list of created events for the stack should be possible."""
|
||||
events = self.client.list_events(self.stack_identifier)['events']
|
||||
self.assertIsInstance(events, list)
|
||||
|
||||
for event in events:
|
||||
self.assert_fields_in_dict(event, 'logical_resource_id', 'id',
|
||||
'resource_status_reason',
|
||||
'resource_status', 'event_time')
|
||||
|
||||
resource_statuses = [event['resource_status'] for event in events]
|
||||
self.assertIn('CREATE_IN_PROGRESS', resource_statuses)
|
||||
self.assertIn('CREATE_COMPLETE', resource_statuses)
|
||||
|
||||
@decorators.idempotent_id('92465723-1673-400a-909d-4773757a3f21')
|
||||
def test_show_event(self):
|
||||
"""Getting details about an event should be possible."""
|
||||
events = self.client.list_resource_events(self.stack_identifier,
|
||||
self.resource_name)['events']
|
||||
self.assertNotEqual([], events)
|
||||
events.sort(key=lambda event: event['event_time'])
|
||||
event_id = events[0]['id']
|
||||
event = self.client.show_event(self.stack_identifier,
|
||||
self.resource_name, event_id)['event']
|
||||
self.assertIsInstance(event, dict)
|
||||
self.assert_fields_in_dict(event, 'resource_name', 'event_time',
|
||||
'links', 'logical_resource_id',
|
||||
'resource_status', 'resource_status_reason',
|
||||
'physical_resource_id', 'id',
|
||||
'resource_properties', 'resource_type')
|
||||
self.assertEqual(self.resource_name, event['resource_name'])
|
||||
self.assertEqual('state changed', event['resource_status_reason'])
|
||||
self.assertEqual(self.resource_name, event['logical_resource_id'])
|
@ -1,92 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class NovaKeyPairResourcesYAMLTest(base.BaseOrchestrationTest):
|
||||
_tpl_type = 'yaml'
|
||||
_resource = 'resources'
|
||||
_type = 'type'
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(NovaKeyPairResourcesYAMLTest, cls).resource_setup()
|
||||
cls.stack_name = data_utils.rand_name('heat')
|
||||
template = cls.read_template('nova_keypair', ext=cls._tpl_type)
|
||||
|
||||
# create the stack, avoid any duplicated key.
|
||||
cls.stack_identifier = cls.create_stack(
|
||||
cls.stack_name,
|
||||
template,
|
||||
parameters={
|
||||
'KeyPairName1': cls.stack_name + '_1',
|
||||
'KeyPairName2': cls.stack_name + '_2'
|
||||
})
|
||||
|
||||
cls.stack_id = cls.stack_identifier.split('/')[1]
|
||||
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
|
||||
resources = (cls.client.list_resources(cls.stack_identifier)
|
||||
['resources'])
|
||||
cls.test_resources = {}
|
||||
for resource in resources:
|
||||
cls.test_resources[resource['logical_resource_id']] = resource
|
||||
|
||||
@decorators.idempotent_id('b476eac2-a302-4815-961f-18c410a2a537')
|
||||
def test_created_resources(self):
|
||||
"""Verifies created keypair resource."""
|
||||
|
||||
nova_keypair_template = self.load_template('nova_keypair',
|
||||
ext=self._tpl_type)
|
||||
resources = [('KeyPairSavePrivate',
|
||||
nova_keypair_template[self._resource][
|
||||
'KeyPairSavePrivate'][self._type]),
|
||||
('KeyPairDontSavePrivate',
|
||||
nova_keypair_template[self._resource][
|
||||
'KeyPairDontSavePrivate'][self._type])]
|
||||
|
||||
for resource_name, resource_type in resources:
|
||||
resource = self.test_resources.get(resource_name, None)
|
||||
self.assertIsInstance(resource, dict)
|
||||
self.assertEqual(resource_name, resource['logical_resource_id'])
|
||||
self.assertEqual(resource_type, resource['resource_type'])
|
||||
self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
|
||||
|
||||
@decorators.idempotent_id('8d77dec7-91fd-45a6-943d-5abd45e338a4')
|
||||
def test_stack_keypairs_output(self):
|
||||
stack = self.client.show_stack(self.stack_name)['stack']
|
||||
self.assertIsInstance(stack, dict)
|
||||
|
||||
output_map = {}
|
||||
for outputs in stack['outputs']:
|
||||
output_map[outputs['output_key']] = outputs['output_value']
|
||||
# Test that first key generated public and private keys
|
||||
self.assertIn('KeyPair_PublicKey', output_map)
|
||||
self.assertIn("Generated", output_map['KeyPair_PublicKey'])
|
||||
self.assertIn('KeyPair_PrivateKey', output_map)
|
||||
self.assertIn('-----BEGIN', output_map['KeyPair_PrivateKey'])
|
||||
# Test that second key generated public key, and private key is not
|
||||
# in the output due to save_private_key = false
|
||||
self.assertIn('KeyPairDontSavePrivate_PublicKey', output_map)
|
||||
self.assertIn('Generated',
|
||||
output_map['KeyPairDontSavePrivate_PublicKey'])
|
||||
self.assertIn(u'KeyPairDontSavePrivate_PrivateKey', output_map)
|
||||
private_key = output_map['KeyPairDontSavePrivate_PrivateKey']
|
||||
self.assertEqual(0, len(private_key))
|
||||
|
||||
|
||||
class NovaKeyPairResourcesAWSTest(NovaKeyPairResourcesYAMLTest):
|
||||
_tpl_type = 'json'
|
||||
_resource = 'Resources'
|
||||
_type = 'Type'
|
@ -1,49 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class ResourceTypesTest(base.BaseOrchestrationTest):
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('7123d082-3577-4a30-8f00-f805327c4ffd')
|
||||
def test_resource_type_list(self):
|
||||
"""Verify it is possible to list resource types."""
|
||||
resource_types = self.client.list_resource_types()['resource_types']
|
||||
self.assertIsInstance(resource_types, list)
|
||||
self.assertIn('OS::Nova::Server', resource_types)
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('0e85a483-828b-4a28-a0e3-f0a21809192b')
|
||||
def test_resource_type_show(self):
|
||||
"""Verify it is possible to get schema about resource types."""
|
||||
resource_types = self.client.list_resource_types()['resource_types']
|
||||
self.assertNotEmpty(resource_types)
|
||||
|
||||
for resource_type in resource_types:
|
||||
type_schema = self.client.show_resource_type(resource_type)
|
||||
self.assert_fields_in_dict(type_schema, 'properties',
|
||||
'attributes', 'resource_type')
|
||||
self.assertEqual(resource_type, type_schema['resource_type'])
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('8401821d-65fe-4d43-9fa3-57d5ce3a35c7')
|
||||
def test_resource_type_template(self):
|
||||
"""Verify it is possible to get template about resource types."""
|
||||
type_template = self.client.show_resource_type_template(
|
||||
'OS::Nova::Server')
|
||||
self.assert_fields_in_dict(
|
||||
type_template,
|
||||
'Outputs',
|
||||
'Parameters', 'Resources')
|
@ -1,169 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
class TestSoftwareConfig(base.BaseOrchestrationTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSoftwareConfig, self).setUp()
|
||||
self.configs = []
|
||||
# Add 2 sets of software configuration
|
||||
self.configs.append(self._config_create('a'))
|
||||
self.configs.append(self._config_create('b'))
|
||||
# Create a deployment using config a's id
|
||||
self._deployment_create(self.configs[0]['id'])
|
||||
|
||||
def _config_create(self, suffix):
|
||||
configuration = {'group': 'script',
|
||||
'inputs': [],
|
||||
'outputs': [],
|
||||
'options': {}}
|
||||
configuration['name'] = 'heat_soft_config_%s' % suffix
|
||||
configuration['config'] = '#!/bin/bash echo init-%s' % suffix
|
||||
api_config = self.client.create_software_config(**configuration)
|
||||
configuration['id'] = api_config['software_config']['id']
|
||||
self.addCleanup(self._config_delete, configuration['id'])
|
||||
self._validate_config(configuration, api_config)
|
||||
return configuration
|
||||
|
||||
def _validate_config(self, configuration, api_config):
|
||||
# Assert all expected keys are present with matching data
|
||||
for k in configuration:
|
||||
self.assertEqual(configuration[k],
|
||||
api_config['software_config'][k])
|
||||
|
||||
def _deployment_create(self, config_id):
|
||||
self.server_id = data_utils.rand_name('dummy-server')
|
||||
self.action = 'ACTION_0'
|
||||
self.status = 'STATUS_0'
|
||||
self.input_values = {}
|
||||
self.output_values = []
|
||||
self.status_reason = 'REASON_0'
|
||||
self.signal_transport = 'NO_SIGNAL'
|
||||
self.deployment = self.client.create_software_deploy(
|
||||
self.server_id, config_id, self.action, self.status,
|
||||
self.input_values, self.output_values, self.status_reason,
|
||||
self.signal_transport)
|
||||
self.deployment_id = self.deployment['software_deployment']['id']
|
||||
self.addCleanup(self._deployment_delete, self.deployment_id)
|
||||
|
||||
def _deployment_delete(self, deploy_id):
|
||||
self.client.delete_software_deploy(deploy_id)
|
||||
# Testing that it is really gone
|
||||
self.assertRaises(
|
||||
lib_exc.NotFound, self.client.show_software_deployment,
|
||||
self.deployment_id)
|
||||
|
||||
def _config_delete(self, config_id):
|
||||
self.client.delete_software_config(config_id)
|
||||
# Testing that it is really gone
|
||||
self.assertRaises(
|
||||
lib_exc.NotFound, self.client.show_software_config, config_id)
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('136162ed-9445-4b9c-b7fc-306af8b5da99')
|
||||
def test_get_software_config(self):
|
||||
"""Testing software config get."""
|
||||
for conf in self.configs:
|
||||
api_config = self.client.show_software_config(conf['id'])
|
||||
self._validate_config(conf, api_config)
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('1275c835-c967-4a2c-8d5d-ad533447ed91')
|
||||
def test_get_deployment_list(self):
|
||||
"""Getting a list of all deployments"""
|
||||
deploy_list = self.client.list_software_deployments()
|
||||
deploy_ids = [deploy['id'] for deploy in
|
||||
deploy_list['software_deployments']]
|
||||
self.assertIn(self.deployment_id, deploy_ids)
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('fe7cd9f9-54b1-429c-a3b7-7df8451db913')
|
||||
def test_get_deployment_metadata(self):
|
||||
"""Testing deployment metadata get"""
|
||||
metadata = self.client.show_software_deployment_metadata(
|
||||
self.server_id)
|
||||
conf_ids = [conf['id'] for conf in metadata['metadata']]
|
||||
self.assertIn(self.configs[0]['id'], conf_ids)
|
||||
|
||||
def _validate_deployment(self, action, status, reason, config_id):
|
||||
deployment = self.client.show_software_deployment(self.deployment_id)
|
||||
self.assertEqual(action, deployment['software_deployment']['action'])
|
||||
self.assertEqual(status, deployment['software_deployment']['status'])
|
||||
self.assertEqual(reason,
|
||||
deployment['software_deployment']['status_reason'])
|
||||
self.assertEqual(config_id,
|
||||
deployment['software_deployment']['config_id'])
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('f29d21f3-ed75-47cf-8cdc-ef1bdeb4c674')
|
||||
def test_software_deployment_create_validate(self):
|
||||
"""Testing software deployment was created as expected."""
|
||||
# Asserting that all fields were created
|
||||
self.assert_fields_in_dict(
|
||||
self.deployment['software_deployment'], 'action', 'config_id',
|
||||
'id', 'input_values', 'output_values', 'server_id', 'status',
|
||||
'status_reason')
|
||||
# Testing get for this deployment and verifying parameters
|
||||
self._validate_deployment(self.action, self.status,
|
||||
self.status_reason, self.configs[0]['id'])
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('2ac43ab3-34f2-415d-be2e-eabb4d14ee32')
|
||||
def test_software_deployment_update_no_metadata_change(self):
|
||||
"""Testing software deployment update without metadata change."""
|
||||
metadata = self.client.show_software_deployment_metadata(
|
||||
self.server_id)
|
||||
# Updating values without changing the configuration ID
|
||||
new_action = 'ACTION_1'
|
||||
new_status = 'STATUS_1'
|
||||
new_reason = 'REASON_1'
|
||||
self.client.update_software_deploy(
|
||||
self.deployment_id, self.server_id, self.configs[0]['id'],
|
||||
new_action, new_status, self.input_values, self.output_values,
|
||||
new_reason, self.signal_transport)
|
||||
# Verifying get and that the deployment was updated as expected
|
||||
self._validate_deployment(new_action, new_status,
|
||||
new_reason, self.configs[0]['id'])
|
||||
|
||||
# Metadata should not be changed at this point
|
||||
test_metadata = self.client.show_software_deployment_metadata(
|
||||
self.server_id)
|
||||
for key in metadata['metadata'][0]:
|
||||
self.assertEqual(
|
||||
metadata['metadata'][0][key],
|
||||
test_metadata['metadata'][0][key])
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('92c48944-d79d-4595-a840-8e1a581c1a72')
|
||||
def test_software_deployment_update_with_metadata_change(self):
|
||||
"""Testing software deployment update with metadata change."""
|
||||
metadata = self.client.show_software_deployment_metadata(
|
||||
self.server_id)
|
||||
self.client.update_software_deploy(
|
||||
self.deployment_id, self.server_id, self.configs[1]['id'],
|
||||
self.action, self.status, self.input_values,
|
||||
self.output_values, self.status_reason, self.signal_transport)
|
||||
self._validate_deployment(self.action, self.status,
|
||||
self.status_reason, self.configs[1]['id'])
|
||||
# Metadata should now be changed
|
||||
new_metadata = self.client.show_software_deployment_metadata(
|
||||
self.server_id)
|
||||
# Its enough to test the ID in this case
|
||||
meta_id = metadata['metadata'][0]['id']
|
||||
test_id = new_metadata['metadata'][0]['id']
|
||||
self.assertNotEqual(meta_id, test_id)
|
@ -1,59 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class StacksTestJSON(base.BaseOrchestrationTest):
|
||||
empty_template = "HeatTemplateFormatVersion: '2012-12-12'\n"
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('d35d628c-07f6-4674-85a1-74db9919e986')
|
||||
def test_stack_list_responds(self):
|
||||
stacks = self.client.list_stacks()['stacks']
|
||||
self.assertIsInstance(stacks, list)
|
||||
|
||||
@decorators.attr(type='smoke')
|
||||
@decorators.idempotent_id('10498bd5-a83e-4b62-a817-ce24afe938fe')
|
||||
def test_stack_crud_no_resources(self):
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
|
||||
# create the stack
|
||||
stack_identifier = self.create_stack(
|
||||
stack_name, self.empty_template)
|
||||
stack_id = stack_identifier.split('/')[1]
|
||||
|
||||
# wait for create complete (with no resources it should be instant)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
# check for stack in list
|
||||
stacks = self.client.list_stacks()['stacks']
|
||||
list_ids = list([stack['id'] for stack in stacks])
|
||||
self.assertIn(stack_id, list_ids)
|
||||
|
||||
# fetch the stack
|
||||
stack = self.client.show_stack(stack_identifier)['stack']
|
||||
self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
|
||||
|
||||
# fetch the stack by name
|
||||
stack = self.client.show_stack(stack_name)['stack']
|
||||
self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
|
||||
|
||||
# fetch the stack by id
|
||||
stack = self.client.show_stack(stack_id)['stack']
|
||||
self.assertEqual('CREATE_COMPLETE', stack['stack_status'])
|
||||
|
||||
# delete the stack
|
||||
self.client.delete_stack(stack_identifier)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
|
@ -1,120 +0,0 @@
|
||||
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
|
||||
#
|
||||
# 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.api.orchestration import base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest import test
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class SwiftResourcesTestJSON(base.BaseOrchestrationTest):
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(SwiftResourcesTestJSON, cls).skip_checks()
|
||||
if not CONF.service_available.swift:
|
||||
raise cls.skipException("Swift support is required")
|
||||
|
||||
@classmethod
|
||||
def setup_credentials(cls):
|
||||
super(SwiftResourcesTestJSON, cls).setup_credentials()
|
||||
stack_owner_role = CONF.orchestration.stack_owner_role
|
||||
operator_role = CONF.object_storage.operator_role
|
||||
cls.os = cls.get_client_manager(
|
||||
roles=[stack_owner_role, operator_role])
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(SwiftResourcesTestJSON, cls).setup_clients()
|
||||
cls.account_client = cls.os_primary.account_client
|
||||
cls.container_client = cls.os_primary.container_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(SwiftResourcesTestJSON, cls).resource_setup()
|
||||
cls.stack_name = data_utils.rand_name('heat')
|
||||
template = cls.read_template('swift_basic')
|
||||
# create the stack
|
||||
cls.stack_identifier = cls.create_stack(
|
||||
cls.stack_name,
|
||||
template)
|
||||
cls.stack_id = cls.stack_identifier.split('/')[1]
|
||||
cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
|
||||
cls.test_resources = {}
|
||||
resources = (cls.client.list_resources(cls.stack_identifier)
|
||||
['resources'])
|
||||
for resource in resources:
|
||||
cls.test_resources[resource['logical_resource_id']] = resource
|
||||
|
||||
@decorators.idempotent_id('1a6fe69e-4be4-4990-9a7a-84b6f18019cb')
|
||||
def test_created_resources(self):
|
||||
"""Created stack should be in the list of existing stacks."""
|
||||
swift_basic_template = self.load_template('swift_basic')
|
||||
resources = [('SwiftContainer', swift_basic_template['resources'][
|
||||
'SwiftContainer']['type']),
|
||||
('SwiftContainerWebsite', swift_basic_template[
|
||||
'resources']['SwiftContainerWebsite']['type'])]
|
||||
for resource_name, resource_type in resources:
|
||||
resource = self.test_resources.get(resource_name)
|
||||
self.assertIsInstance(resource, dict)
|
||||
self.assertEqual(resource_type, resource['resource_type'])
|
||||
self.assertEqual(resource_name, resource['logical_resource_id'])
|
||||
self.assertEqual('CREATE_COMPLETE', resource['resource_status'])
|
||||
|
||||
@decorators.idempotent_id('bd438b18-5494-4d5a-9ce6-d2a942ec5060')
|
||||
@test.services('object_storage')
|
||||
def test_created_containers(self):
|
||||
params = {'format': 'json'}
|
||||
_, container_list = \
|
||||
self.account_client.list_account_containers(params=params)
|
||||
created_containers = [cont for cont in container_list
|
||||
if cont['name'].startswith(self.stack_name)]
|
||||
self.assertEqual(2, len(created_containers))
|
||||
|
||||
@decorators.idempotent_id('73d0c093-9922-44a0-8b1d-1fc092dee367')
|
||||
@test.services('object_storage')
|
||||
def test_acl(self):
|
||||
acl_headers = ('x-container-meta-web-index', 'x-container-read')
|
||||
|
||||
swcont = self.test_resources.get(
|
||||
'SwiftContainer')['physical_resource_id']
|
||||
swcont_website = self.test_resources.get(
|
||||
'SwiftContainerWebsite')['physical_resource_id']
|
||||
|
||||
headers, _ = self.container_client.list_container_metadata(swcont)
|
||||
for h in acl_headers:
|
||||
self.assertNotIn(h, headers)
|
||||
headers, _ = self.container_client.list_container_metadata(
|
||||
swcont_website)
|
||||
for h in acl_headers:
|
||||
self.assertIn(h, headers)
|
||||
|
||||
@decorators.idempotent_id('fda06135-6777-4594-aefa-0f6107169698')
|
||||
@test.services('object_storage')
|
||||
def test_metadata(self):
|
||||
swift_basic_template = self.load_template('swift_basic')
|
||||
metadatas = swift_basic_template['resources']['SwiftContainerWebsite'][
|
||||
'properties']['X-Container-Meta']
|
||||
swcont_website = self.test_resources.get(
|
||||
'SwiftContainerWebsite')['physical_resource_id']
|
||||
headers, _ = self.container_client.list_container_metadata(
|
||||
swcont_website)
|
||||
|
||||
for meta in metadatas:
|
||||
header_meta = "x-container-meta-%s" % meta
|
||||
self.assertIn(header_meta, headers)
|
||||
self.assertEqual(headers[header_meta], metadatas[meta])
|
@ -1,61 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
|
||||
class TemplateYAMLTestJSON(base.BaseOrchestrationTest):
|
||||
template = """
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: |
|
||||
Template which creates only a new user
|
||||
Resources:
|
||||
CfnUser:
|
||||
Type: AWS::IAM::User
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TemplateYAMLTestJSON, cls).resource_setup()
|
||||
cls.stack_name = data_utils.rand_name('heat')
|
||||
cls.stack_identifier = cls.create_stack(cls.stack_name, cls.template)
|
||||
cls.client.wait_for_stack_status(cls.stack_identifier,
|
||||
'CREATE_COMPLETE')
|
||||
cls.stack_id = cls.stack_identifier.split('/')[1]
|
||||
cls.parameters = {}
|
||||
|
||||
@decorators.idempotent_id('47430699-c368-495e-a1db-64c26fd967d7')
|
||||
def test_show_template(self):
|
||||
"""Getting template used to create the stack."""
|
||||
self.client.show_template(self.stack_identifier)
|
||||
|
||||
@decorators.idempotent_id('ed53debe-8727-46c5-ab58-eba6090ec4de')
|
||||
def test_validate_template(self):
|
||||
"""Validating template passing it content."""
|
||||
self.client.validate_template(self.template,
|
||||
self.parameters)
|
||||
|
||||
|
||||
class TemplateAWSTestJSON(TemplateYAMLTestJSON):
|
||||
template = """
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Template which creates only a new user",
|
||||
"Resources" : {
|
||||
"CfnUser" : {
|
||||
"Type" : "AWS::IAM::User"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
@ -1,60 +0,0 @@
|
||||
# Copyright 2014 NEC 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.
|
||||
|
||||
from tempest.api.orchestration import base
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
|
||||
class TemplateYAMLNegativeTestJSON(base.BaseOrchestrationTest):
|
||||
template = """
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: |
|
||||
Template which creates only a new user
|
||||
Resources:
|
||||
CfnUser:
|
||||
Type: AWS::IAM::User
|
||||
"""
|
||||
|
||||
invalid_template_url = 'http:///template.yaml'
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(TemplateYAMLNegativeTestJSON, cls).resource_setup()
|
||||
cls.parameters = {}
|
||||
|
||||
@decorators.attr(type=['negative'])
|
||||
@decorators.idempotent_id('5586cbca-ddc4-4152-9db8-fa1ce5fc1876')
|
||||
def test_validate_template_url(self):
|
||||
"""Validating template passing url to it."""
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.client.validate_template_url,
|
||||
template_url=self.invalid_template_url,
|
||||
parameters=self.parameters)
|
||||
|
||||
|
||||
class TemplateAWSNegativeTestJSON(TemplateYAMLNegativeTestJSON):
|
||||
template = """
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Template which creates only a new user",
|
||||
"Resources" : {
|
||||
"CfnUser" : {
|
||||
"Type" : "AWS::IAM::User"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
invalid_template_url = 'http:///template.template'
|
@ -1,116 +0,0 @@
|
||||
# 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.api.orchestration import base
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest import test
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class CinderResourcesTest(base.BaseOrchestrationTest):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(CinderResourcesTest, cls).skip_checks()
|
||||
if not CONF.service_available.cinder:
|
||||
raise cls.skipException('Cinder support is required')
|
||||
|
||||
def _cinder_verify(self, volume_id, template):
|
||||
self.assertIsNotNone(volume_id)
|
||||
volume = self.volumes_client.show_volume(volume_id)['volume']
|
||||
self.assertEqual('available', volume.get('status'))
|
||||
self.assertEqual(CONF.volume.volume_size, volume.get('size'))
|
||||
|
||||
self.assertEqual(template['resources']['volume']['properties'][
|
||||
'description'], volume.get('description'))
|
||||
self.assertEqual(template['resources']['volume']['properties'][
|
||||
'name'], volume.get('name'))
|
||||
|
||||
def _outputs_verify(self, stack_identifier, template):
|
||||
self.assertEqual('available',
|
||||
self.get_stack_output(stack_identifier, 'status'))
|
||||
self.assertEqual(str(CONF.volume.volume_size),
|
||||
self.get_stack_output(stack_identifier, 'size'))
|
||||
self.assertEqual(template['resources']['volume']['properties'][
|
||||
'description'], self.get_stack_output(stack_identifier,
|
||||
'display_description'))
|
||||
self.assertEqual(template['resources']['volume']['properties'][
|
||||
'name'], self.get_stack_output(stack_identifier, 'display_name'))
|
||||
|
||||
@decorators.idempotent_id('c3243329-7bdd-4730-b402-4d19d50c41d8')
|
||||
@test.services('volume')
|
||||
def test_cinder_volume_create_delete(self):
|
||||
"""Create and delete a volume via OS::Cinder::Volume."""
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
template = self.read_template('cinder_basic')
|
||||
stack_identifier = self.create_stack(
|
||||
stack_name,
|
||||
template,
|
||||
parameters={
|
||||
'volume_size': CONF.volume.volume_size
|
||||
})
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
# Verify with cinder that the volume exists, with matching details
|
||||
volume_id = self.get_stack_output(stack_identifier, 'volume_id')
|
||||
cinder_basic_template = self.load_template('cinder_basic')
|
||||
self._cinder_verify(volume_id, cinder_basic_template)
|
||||
|
||||
# Verify the stack outputs are as expected
|
||||
self._outputs_verify(stack_identifier, cinder_basic_template)
|
||||
|
||||
# Delete the stack and ensure the volume is gone
|
||||
self.client.delete_stack(stack_identifier)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.volumes_client.show_volume,
|
||||
volume_id)
|
||||
|
||||
def _cleanup_volume(self, volume_id):
|
||||
"""Cleanup the volume direct with cinder."""
|
||||
self.volumes_client.delete_volume(volume_id)
|
||||
self.volumes_client.wait_for_resource_deletion(volume_id)
|
||||
|
||||
@decorators.idempotent_id('ea8b3a46-b932-4c18-907a-fe23f00b33f8')
|
||||
@test.services('volume')
|
||||
def test_cinder_volume_create_delete_retain(self):
|
||||
"""Ensure the 'Retain' deletion policy is respected."""
|
||||
stack_name = data_utils.rand_name('heat')
|
||||
template = self.read_template('cinder_basic_delete_retain')
|
||||
stack_identifier = self.create_stack(
|
||||
stack_name,
|
||||
template,
|
||||
parameters={
|
||||
'volume_size': CONF.volume.volume_size
|
||||
})
|
||||
self.client.wait_for_stack_status(stack_identifier, 'CREATE_COMPLETE')
|
||||
|
||||
# Verify with cinder that the volume exists, with matching details
|
||||
volume_id = self.get_stack_output(stack_identifier, 'volume_id')
|
||||
self.addCleanup(self._cleanup_volume, volume_id)
|
||||
retain_template = self.load_template('cinder_basic_delete_retain')
|
||||
self._cinder_verify(volume_id, retain_template)
|
||||
|
||||
# Verify the stack outputs are as expected
|
||||
self._outputs_verify(stack_identifier, retain_template)
|
||||
|
||||
# Delete the stack and ensure the volume is *not* gone
|
||||
self.client.delete_stack(stack_identifier)
|
||||
self.client.wait_for_stack_status(stack_identifier, 'DELETE_COMPLETE')
|
||||
self._cinder_verify(volume_id, retain_template)
|
||||
|
||||
# Volume cleanup happens via addCleanup calling _cleanup_volume
|
Loading…
Reference in New Issue
Block a user