Create reusable Heat staks.
Change-Id: I51a62f21ad29068faffca95087833fed9e3806cd
This commit is contained in:
parent
30f336a736
commit
03cd053a61
24
tobiko/openstack/stacks/__init__.py
Normal file
24
tobiko/openstack/stacks/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
from tobiko.openstack.stacks import _neutron
|
||||
from tobiko.openstack.stacks import _nova
|
||||
|
||||
NovaKeyPairStackFixture = _nova.NovaKeyPairStackFixture
|
||||
|
||||
NeutronNetworkStackFixture = _neutron.NeutronNetworkStackFixture
|
||||
NeutronServerStackFixture = _neutron.NeutronServerStackFixture
|
36
tobiko/openstack/stacks/_hot.py
Normal file
36
tobiko/openstack/stacks/_hot.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from tobiko.openstack import heat
|
||||
from tobiko import config
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
TEMPLATE_DIRS = [os.path.dirname(__file__)]
|
||||
|
||||
|
||||
def heat_template_file(template_file):
|
||||
"""Fixture to load template files from templates directory
|
||||
|
||||
Return fixtures to loads templates from
|
||||
'tobiko/tests/scenario/neutron/templates' directory
|
||||
"""
|
||||
return heat.heat_template_file(template_file=template_file,
|
||||
template_dirs=TEMPLATE_DIRS)
|
107
tobiko/openstack/stacks/_neutron.py
Normal file
107
tobiko/openstack/stacks/_neutron.py
Normal file
@ -0,0 +1,107 @@
|
||||
# Copyright (c) 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
|
||||
import tobiko
|
||||
from tobiko import config
|
||||
from tobiko.openstack import heat
|
||||
from tobiko.openstack.stacks import _hot
|
||||
from tobiko.openstack.stacks import _nova
|
||||
from tobiko.shell import ssh
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class NeutronNetworkStackFixture(heat.HeatStackFixture):
|
||||
"""Heat stack for creating internal network with a router to external
|
||||
|
||||
"""
|
||||
|
||||
#: Heat template file
|
||||
template = _hot.heat_template_file('neutron/network.yaml')
|
||||
|
||||
#: IPv4 sub-net CIDR
|
||||
ipv4_cidr = '190.40.2.0/24'
|
||||
|
||||
@property
|
||||
def has_ipv4(self):
|
||||
return bool(self.ipv4_cidr)
|
||||
|
||||
#: IPv6 sub-net CIDR
|
||||
ipv6_cidr = '2001:db8:1:2::/64'
|
||||
|
||||
@property
|
||||
def has_ipv6(self):
|
||||
return bool(self.ipv6_cidr)
|
||||
|
||||
#: Floating IP network where the Neutron floating IPs are created
|
||||
gateway_network = CONF.tobiko.neutron.floating_network
|
||||
|
||||
@property
|
||||
def has_gateway(self):
|
||||
return bool(self.gateway_network)
|
||||
|
||||
|
||||
class NeutronServerStackFixture(heat.HeatStackFixture):
|
||||
|
||||
#: Heat template file
|
||||
template = _hot.heat_template_file('neutron/server.yaml')
|
||||
|
||||
key_pair_stack = tobiko.required_setup_fixture(
|
||||
_nova.NovaKeyPairStackFixture)
|
||||
network_stack = tobiko.required_setup_fixture(NeutronNetworkStackFixture)
|
||||
|
||||
#: Glance image used to create a Nova server instance
|
||||
image = CONF.tobiko.nova.image
|
||||
|
||||
#: Nova flavor used to create a Nova server instance
|
||||
flavor = CONF.tobiko.nova.flavor
|
||||
|
||||
#: username used to login to a Nova server instance
|
||||
username = CONF.tobiko.nova.username
|
||||
|
||||
#: password used to login to a Nova server instance
|
||||
password = CONF.tobiko.nova.password
|
||||
|
||||
@property
|
||||
def key_name(self):
|
||||
return self.key_pair_stack.outputs.key_name
|
||||
|
||||
@property
|
||||
def network(self):
|
||||
return self.network_stack.outputs.network_id
|
||||
|
||||
#: Floating IP network where the Neutron floating IP is created
|
||||
floating_network = CONF.tobiko.neutron.floating_network
|
||||
|
||||
@property
|
||||
def has_floating_ip(self):
|
||||
return bool(self.floating_network)
|
||||
|
||||
@property
|
||||
def ssh_client(self):
|
||||
return ssh.ssh_client(
|
||||
host=self.outputs.floating_ip_address,
|
||||
username=self.username,
|
||||
password=self.password)
|
||||
|
||||
@property
|
||||
def ssh_command(self):
|
||||
return ssh.ssh_command(
|
||||
host=self.outputs.floating_ip_address,
|
||||
username=self.username)
|
51
tobiko/openstack/stacks/_nova.py
Normal file
51
tobiko/openstack/stacks/_nova.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright (c) 2019 Red Hat, Inc.
|
||||
#
|
||||
# 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 __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
import six
|
||||
|
||||
from tobiko import config
|
||||
from tobiko.openstack import heat
|
||||
from tobiko.openstack.stacks import _hot
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class NovaKeyPairStackFixture(heat.HeatStackFixture):
|
||||
template = _hot.heat_template_file('nova/key_pair.yaml')
|
||||
key_file = os.path.expanduser(CONF.tobiko.nova.key_file)
|
||||
public_key = None
|
||||
private_key = None
|
||||
|
||||
def setup_fixture(self):
|
||||
self.read_keys()
|
||||
super(NovaKeyPairStackFixture, self).setup_fixture()
|
||||
|
||||
def read_keys(self):
|
||||
with open(self.key_file, 'r') as fd:
|
||||
self.private_key = as_str(fd.read())
|
||||
with open(self.key_file + '.pub', 'r') as fd:
|
||||
self.public_key = as_str(fd.read())
|
||||
|
||||
|
||||
def as_str(text):
|
||||
if isinstance(text, six.string_types):
|
||||
return text
|
||||
else:
|
||||
return text.decode()
|
185
tobiko/openstack/stacks/neutron/network.yaml
Normal file
185
tobiko/openstack/stacks/neutron/network.yaml
Normal file
@ -0,0 +1,185 @@
|
||||
heat_template_version: newton
|
||||
|
||||
|
||||
description: |
|
||||
Creates an network with a subnet and a gateway router to an external network
|
||||
if given
|
||||
|
||||
|
||||
parameters:
|
||||
port_security_enabled:
|
||||
description: Default value to be assigned to network ports
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
value_specs:
|
||||
description: Extra network creation parameters
|
||||
type: json
|
||||
default: {}
|
||||
|
||||
has_ipv4:
|
||||
description: Whenever to create IPv4 subnet
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
has_ipv6:
|
||||
description: Whenever to create IPv6 subnet
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
ipv4_cidr:
|
||||
description: IPv4 subnet CIDR to be assigned to new network
|
||||
type: string
|
||||
constraints:
|
||||
- custom_constraint: net_cidr
|
||||
|
||||
ipv6_cidr:
|
||||
description: IPv6 subnet CIDR to be assigned to new network
|
||||
type: string
|
||||
constraints:
|
||||
- custom_constraint: net_cidr
|
||||
|
||||
ipv4_dns_nameservers:
|
||||
description: IPv4 nameservers IP addresses
|
||||
type: comma_delimited_list
|
||||
default: []
|
||||
constraints:
|
||||
- custom_constraint: dns_name
|
||||
|
||||
ipv6_dns_nameservers:
|
||||
description: IPv6 nameservers IP addresses
|
||||
type: comma_delimited_list
|
||||
default: []
|
||||
constraints:
|
||||
- custom_constraint: dns_name
|
||||
|
||||
ipv6_address_mode:
|
||||
description: IPv6 address mode
|
||||
type: string
|
||||
default: slaac
|
||||
constraints:
|
||||
- allowed_values: [ slaac, dhcpv6-stateful, dhcpv6-stateless ]
|
||||
|
||||
ipv6_ra_mode:
|
||||
description: IPv6 router advertisement mode
|
||||
type: string
|
||||
default: slaac
|
||||
constraints:
|
||||
- allowed_values: [ slaac, dhcpv6-stateful, dhcpv6-stateless ]
|
||||
|
||||
has_gateway:
|
||||
description: whenever to create gateway router
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
gateway_network:
|
||||
description: Optional gateway network to route packages to
|
||||
type: string
|
||||
default:
|
||||
constraints:
|
||||
- custom_constraint: neutron.network
|
||||
|
||||
has_net_mtu:
|
||||
description: whenever net mtu extension is available
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
|
||||
conditions:
|
||||
has_ipv4:
|
||||
get_param: has_ipv4
|
||||
|
||||
has_ipv6:
|
||||
get_param: has_ipv6
|
||||
|
||||
has_gateway:
|
||||
get_param: has_gateway
|
||||
|
||||
has_ipv4_gateway:
|
||||
and:
|
||||
- get_param: has_ipv4
|
||||
- get_param: has_gateway
|
||||
|
||||
has_ipv6_gateway:
|
||||
and:
|
||||
- get_param: has_ipv6
|
||||
- get_param: has_gateway
|
||||
|
||||
has_net_mtu:
|
||||
get_param: has_net_mtu
|
||||
|
||||
|
||||
resources:
|
||||
|
||||
network:
|
||||
type: OS::Neutron::Net
|
||||
properties:
|
||||
port_security_enabled: {get_param: port_security_enabled}
|
||||
value_specs: {get_param: value_specs}
|
||||
|
||||
ipv4_subnet:
|
||||
type: OS::Neutron::Subnet
|
||||
condition: has_ipv4
|
||||
properties:
|
||||
network: {get_resource: network}
|
||||
ip_version: 4
|
||||
cidr: {get_param: ipv4_cidr}
|
||||
dns_nameservers: {get_param: ipv4_dns_nameservers}
|
||||
|
||||
ipv6_subnet:
|
||||
type: OS::Neutron::Subnet
|
||||
condition: has_ipv6
|
||||
properties:
|
||||
network: {get_resource: network}
|
||||
ip_version: 6
|
||||
cidr: {get_param: ipv6_cidr}
|
||||
dns_nameservers: {get_param: ipv6_dns_nameservers}
|
||||
ipv6_address_mode: {get_param: ipv6_address_mode}
|
||||
ipv6_ra_mode: {get_param: ipv6_ra_mode}
|
||||
|
||||
gateway:
|
||||
type: OS::Neutron::Router
|
||||
condition: has_gateway
|
||||
properties:
|
||||
external_gateway_info:
|
||||
network: {get_param: gateway_network}
|
||||
|
||||
ipv4_gateway_interface:
|
||||
type: OS::Neutron::RouterInterface
|
||||
condition: has_ipv4_gateway
|
||||
properties:
|
||||
router: {get_resource: gateway}
|
||||
subnet: {get_resource: ipv4_subnet}
|
||||
|
||||
ipv6_gateway_interface:
|
||||
type: OS::Neutron::RouterInterface
|
||||
condition: has_ipv6_gateway
|
||||
properties:
|
||||
router: {get_resource: gateway}
|
||||
subnet: {get_resource: ipv6_subnet}
|
||||
|
||||
outputs:
|
||||
|
||||
network_id:
|
||||
description: Network ID
|
||||
value: {get_resource: network}
|
||||
|
||||
ipv4_subnet_id:
|
||||
description: IPv4 subnet ID
|
||||
value: {get_resource: ipv4_subnet}
|
||||
condition: has_ipv4
|
||||
|
||||
ipv6_subnet_id:
|
||||
description: IPv6 subnet ID
|
||||
value: {get_resource: ipv6_subnet}
|
||||
condition: has_ipv6
|
||||
|
||||
gateway_id:
|
||||
description: Gateway router ID
|
||||
value: {get_resource: gateway}
|
||||
condition: has_gateway
|
||||
|
||||
mtu:
|
||||
description: Network MTU value (integer)
|
||||
value: {get_attr: [network, mtu]}
|
||||
condition: has_net_mtu
|
35
tobiko/openstack/stacks/neutron/security_groups.yaml
Normal file
35
tobiko/openstack/stacks/neutron/security_groups.yaml
Normal file
@ -0,0 +1,35 @@
|
||||
heat_template_version: newton
|
||||
|
||||
|
||||
description: |
|
||||
Stack of shared Neutron security groups
|
||||
|
||||
|
||||
resources:
|
||||
|
||||
icmp:
|
||||
type: OS::Neutron::SecurityGroup
|
||||
description: Security group to allow to ping Nova server instances
|
||||
properties:
|
||||
rules:
|
||||
- protocol: icmp
|
||||
|
||||
ssh:
|
||||
type: OS::Neutron::SecurityGroup
|
||||
description: Security group to allow to SSH Nova server instances
|
||||
properties:
|
||||
rules:
|
||||
- protocol: tcp
|
||||
port_range_min: 22
|
||||
port_range_max: 22
|
||||
|
||||
|
||||
outputs:
|
||||
|
||||
icmp_security_group_id:
|
||||
description: Security group ID to allow to ping Nova server instances
|
||||
value: {get_resource: icmp}
|
||||
|
||||
ssh_security_group_id:
|
||||
description: Security group ID to allow to SSH Nova server instances
|
||||
value: {get_resource: ssh}
|
117
tobiko/openstack/stacks/neutron/server.yaml
Normal file
117
tobiko/openstack/stacks/neutron/server.yaml
Normal file
@ -0,0 +1,117 @@
|
||||
heat_template_version: newton
|
||||
|
||||
|
||||
description: |
|
||||
Creates a Nova server connected to an existing Neutron network and
|
||||
optionally assign a floating IP address to server so it is routable from the
|
||||
public network.
|
||||
|
||||
|
||||
parameters:
|
||||
|
||||
key_name:
|
||||
type: string
|
||||
description: Name of keypair to assign to server
|
||||
constraints:
|
||||
- custom_constraint: nova.keypair
|
||||
|
||||
flavor:
|
||||
type: string
|
||||
description: Flavor to use for server
|
||||
constraints:
|
||||
- custom_constraint: nova.flavor
|
||||
|
||||
image:
|
||||
type: string
|
||||
description: Name of image to use for server
|
||||
|
||||
network:
|
||||
type: string
|
||||
description: ID of network to which server get connected
|
||||
constraints:
|
||||
- custom_constraint: neutron.network
|
||||
|
||||
port_security_enabled:
|
||||
type: boolean
|
||||
description: Whenever port security is enabled on server port
|
||||
default: false
|
||||
|
||||
security_groups:
|
||||
type: comma_delimited_list
|
||||
description: Security groups to subscrive server port
|
||||
default: []
|
||||
|
||||
has_floating_ip:
|
||||
type: boolean
|
||||
description: Whenever server has floating IP associated
|
||||
default: false
|
||||
|
||||
floating_network:
|
||||
type: string
|
||||
description: |
|
||||
Public network for which floating IP addresses will be allocated
|
||||
constraints:
|
||||
- custom_constraint: neutron.network
|
||||
|
||||
|
||||
conditions:
|
||||
|
||||
has_floating_ip:
|
||||
get_param: has_floating_ip
|
||||
|
||||
|
||||
resources:
|
||||
|
||||
port:
|
||||
type: OS::Neutron::Port
|
||||
description: Neutron port
|
||||
properties:
|
||||
network: {get_param: network}
|
||||
port_security_enabled: {get_param: port_security_enabled}
|
||||
security_groups: {get_param: security_groups}
|
||||
|
||||
server_name:
|
||||
type: OS::Heat::RandomString
|
||||
properties:
|
||||
character_classes: [{'class': 'lowercase', 'min': 1}]
|
||||
length: 8
|
||||
|
||||
server:
|
||||
type: OS::Nova::Server
|
||||
description: Nova server connected to Neutron port
|
||||
properties:
|
||||
name: {get_attr: [server_name, value]}
|
||||
key_name: {get_param: key_name}
|
||||
image: {get_param: image}
|
||||
flavor: {get_param: flavor}
|
||||
networks:
|
||||
- port: {get_resource: port}
|
||||
|
||||
floating_ip:
|
||||
type: OS::Neutron::FloatingIP
|
||||
description: Floating IP address to be connected to server
|
||||
condition: has_floating_ip
|
||||
properties:
|
||||
floating_network: {get_param: floating_network}
|
||||
port_id: {get_resource: port}
|
||||
|
||||
|
||||
outputs:
|
||||
|
||||
fixed_ips:
|
||||
description: fixed IP addresses of server
|
||||
value: {get_attr: [port, fixed_ips]}
|
||||
|
||||
floating_ip_address:
|
||||
description: Floating IP address of server in public network
|
||||
value: { get_attr: [ floating_ip, floating_ip_address ] }
|
||||
condition: has_floating_ip
|
||||
|
||||
port_security_enabled:
|
||||
value: {get_attr: [port, port_security_enabled]}
|
||||
|
||||
security_groups:
|
||||
value: {get_attr: [port, security_groups]}
|
||||
|
||||
server_name:
|
||||
value: {get_attr: [server, name]}
|
34
tobiko/openstack/stacks/nova/key_pair.yaml
Normal file
34
tobiko/openstack/stacks/nova/key_pair.yaml
Normal file
@ -0,0 +1,34 @@
|
||||
heat_template_version: newton
|
||||
|
||||
description: |
|
||||
Creates a nova SSH keypair to be used for creating Nova servers
|
||||
|
||||
|
||||
parameters:
|
||||
|
||||
public_key:
|
||||
type: string
|
||||
description: SSH public key
|
||||
|
||||
|
||||
resources:
|
||||
|
||||
key_name:
|
||||
type: OS::Heat::RandomString
|
||||
description: Random unique key pair name
|
||||
properties:
|
||||
length: 32
|
||||
|
||||
key_pair:
|
||||
type: OS::Nova::KeyPair
|
||||
description: SSH key pair
|
||||
properties:
|
||||
name: {get_attr: [key_name, value]}
|
||||
public_key: {get_param: public_key}
|
||||
|
||||
|
||||
outputs:
|
||||
|
||||
key_name:
|
||||
description: unique Nova key pair name
|
||||
value: {get_attr: [key_name, value]}
|
@ -17,7 +17,9 @@ from __future__ import absolute_import
|
||||
|
||||
import testtools
|
||||
|
||||
import tobiko
|
||||
from tobiko import config
|
||||
from tobiko.openstack import stacks
|
||||
from tobiko.shell import sh
|
||||
|
||||
|
||||
@ -59,14 +61,17 @@ class ExecuteTest(testtools.TestCase):
|
||||
def test_succeed_with_timeout(self):
|
||||
self.test_succeed(timeout=30.)
|
||||
|
||||
def test_fails(self, command='false', exit_status=1, stdout='', stderr='',
|
||||
**kwargs):
|
||||
def test_fails(self, command='false', exit_status=None, stdout='',
|
||||
stderr='', **kwargs):
|
||||
ex = self.assertRaises(sh.ShellCommandFailed, self.execute, command,
|
||||
**kwargs)
|
||||
self.assertEqual(self.expected_ex_command(command), ex.command)
|
||||
self.assertEqual(stdout, ex.stdout)
|
||||
self.assertEqual(stderr, ex.stderr)
|
||||
self.assertEqual(exit_status, ex.exit_status)
|
||||
if exit_status:
|
||||
self.assertEqual(exit_status, ex.exit_status)
|
||||
else:
|
||||
self.assertTrue(ex.exit_status)
|
||||
|
||||
def test_fails_getting_exit_status(self):
|
||||
self.test_fails('exit 15', exit_status=15)
|
||||
@ -103,3 +108,23 @@ class ExecuteTest(testtools.TestCase):
|
||||
|
||||
def expected_ex_command(self, command):
|
||||
return sh.join_command(self.expected_command(command))
|
||||
|
||||
|
||||
class ExecuteWithSSHClientTest(ExecuteTest):
|
||||
|
||||
server_stack = tobiko.required_setup_fixture(
|
||||
stacks.NeutronServerStackFixture)
|
||||
|
||||
@property
|
||||
def ssh_client(self):
|
||||
return self.server_stack.ssh_client
|
||||
|
||||
|
||||
class ExecuteWithSSHCommandTest(ExecuteTest):
|
||||
|
||||
server_stack = tobiko.required_setup_fixture(
|
||||
stacks.NeutronServerStackFixture)
|
||||
|
||||
@property
|
||||
def shell(self):
|
||||
return self.server_stack.ssh_command
|
||||
|
Loading…
Reference in New Issue
Block a user