heat/heat/tests/openstack/neutron/test_neutron_trunk.py

466 lines
17 KiB
Python

# Copyright 2017 Ericsson
#
# 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 copy
import six
from oslo_log import log as logging
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.resources.openstack.neutron import trunk
from heat.engine import scheduler
from heat.engine import stk_defn
from heat.tests import common
from heat.tests import utils
from neutronclient.common import exceptions as ncex
from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.v2_0 import client as neutronclient
LOG = logging.getLogger(__name__)
create_template = '''
heat_template_version: 2017-09-01
description: Template to test Neutron Trunk resource
resources:
parent_port:
type: OS::Neutron::Port
properties:
network: parent_port_net
subport_1:
type: OS::Neutron::Port
properties:
network: subport_1_net
subport_2:
type: OS::Neutron::Port
properties:
network: subport_2_net
trunk:
type: OS::Neutron::Trunk
properties:
name: trunk name
description: trunk description
port: { get_resource: parent_port }
sub_ports:
- { port: { get_resource: subport_1 },
segmentation_type: vlan,
segmentation_id: 101 }
- { port: { get_resource: subport_2 },
segmentation_type: vlan,
segmentation_id: 102 }
'''
update_template = '''
heat_template_version: 2017-09-01
description: Template to test Neutron Trunk resource
resources:
trunk:
type: OS::Neutron::Trunk
properties:
name: trunk name
description: trunk description
port: parent_port_id
sub_ports:
- { port: subport_1_id,
segmentation_type: vlan,
segmentation_id: 101 }
- { port: subport_2_id,
segmentation_type: vlan,
segmentation_id: 102 }
'''
class NeutronTrunkTest(common.HeatTestCase):
def setUp(self):
super(NeutronTrunkTest, self).setUp()
self.patchobject(
neutron.NeutronClientPlugin, 'has_extension', return_value=True)
self.create_trunk_mock = self.patchobject(
neutronclient.Client, 'create_trunk')
self.delete_trunk_mock = self.patchobject(
neutronclient.Client, 'delete_trunk')
self.show_trunk_mock = self.patchobject(
neutronclient.Client, 'show_trunk')
self.update_trunk_mock = self.patchobject(
neutronclient.Client, 'update_trunk')
self.trunk_remove_subports_mock = self.patchobject(
neutronclient.Client, 'trunk_remove_subports')
self.trunk_add_subports_mock = self.patchobject(
neutronclient.Client, 'trunk_add_subports')
self.find_resource_mock = self.patchobject(
neutronV20, 'find_resourceid_by_name_or_id')
rv = {
'trunk': {
'id': 'trunk id',
'status': 'DOWN',
}
}
self.create_trunk_mock.return_value = rv
self.show_trunk_mock.return_value = rv
def find_resourceid_by_name_or_id(
_client, _resource, name_or_id, **_kwargs):
return name_or_id
self.find_resource_mock.side_effect = find_resourceid_by_name_or_id
def _create_trunk(self, stack):
trunk = stack['trunk']
scheduler.TaskRunner(trunk.create)()
stk_defn.update_resource_data(stack.defn, trunk.name,
trunk.node_data())
self.assertEqual((trunk.CREATE, trunk.COMPLETE), trunk.state)
def _delete_trunk(self, stack):
trunk = stack['trunk']
scheduler.TaskRunner(trunk.delete)()
self.assertEqual((trunk.DELETE, trunk.COMPLETE), trunk.state)
def test_create_missing_port_property(self):
t = template_format.parse(create_template)
del t['resources']['trunk']['properties']['port']
stack = utils.parse_stack(t)
self.assertRaises(
exception.StackValidationFailed,
stack.validate)
def test_create_no_subport(self):
t = template_format.parse(create_template)
del t['resources']['trunk']['properties']['sub_ports']
del t['resources']['subport_1']
del t['resources']['subport_2']
stack = utils.parse_stack(t)
parent_port = stack['parent_port']
self.patchobject(parent_port, 'get_reference_id',
return_value='parent port id')
self.find_resource_mock.return_value = 'parent port id'
stk_defn.update_resource_data(stack.defn, parent_port.name,
parent_port.node_data())
self._create_trunk(stack)
self.create_trunk_mock.assert_called_once_with({
'trunk': {
'description': 'trunk description',
'name': 'trunk name',
'port_id': 'parent port id',
}}
)
def test_create_one_subport(self):
t = template_format.parse(create_template)
del t['resources']['trunk']['properties']['sub_ports'][1:]
del t['resources']['subport_2']
stack = utils.parse_stack(t)
parent_port = stack['parent_port']
self.patchobject(parent_port, 'get_reference_id',
return_value='parent port id')
stk_defn.update_resource_data(stack.defn, parent_port.name,
parent_port.node_data())
subport_1 = stack['subport_1']
self.patchobject(subport_1, 'get_reference_id',
return_value='subport id')
stk_defn.update_resource_data(stack.defn, subport_1.name,
subport_1.node_data())
self._create_trunk(stack)
self.create_trunk_mock.assert_called_once_with({
'trunk': {
'description': 'trunk description',
'name': 'trunk name',
'port_id': 'parent port id',
'sub_ports': [
{'port_id': 'subport id',
'segmentation_type': 'vlan',
'segmentation_id': 101},
],
}}
)
def test_create_two_subports(self):
t = template_format.parse(create_template)
del t['resources']['trunk']['properties']['sub_ports'][2:]
stack = utils.parse_stack(t)
parent_port = stack['parent_port']
self.patchobject(parent_port, 'get_reference_id',
return_value='parent_port_id')
stk_defn.update_resource_data(stack.defn, parent_port.name,
parent_port.node_data())
subport_1 = stack['subport_1']
self.patchobject(subport_1, 'get_reference_id',
return_value='subport_1_id')
stk_defn.update_resource_data(stack.defn, subport_1.name,
subport_1.node_data())
subport_2 = stack['subport_2']
self.patchobject(subport_2, 'get_reference_id',
return_value='subport_2_id')
stk_defn.update_resource_data(stack.defn, subport_2.name,
subport_2.node_data())
self._create_trunk(stack)
self.create_trunk_mock.assert_called_once_with({
'trunk': {
'description': 'trunk description',
'name': 'trunk name',
'port_id': 'parent_port_id',
'sub_ports': [
{'port_id': 'subport_1_id',
'segmentation_type': 'vlan',
'segmentation_id': 101},
{'port_id': 'subport_2_id',
'segmentation_type': 'vlan',
'segmentation_id': 102},
],
}}
)
def test_create_degraded(self):
t = template_format.parse(create_template)
stack = utils.parse_stack(t)
rv = {
'trunk': {
'id': 'trunk id',
'status': 'DEGRADED',
}
}
self.create_trunk_mock.return_value = rv
self.show_trunk_mock.return_value = rv
trunk = stack['trunk']
e = self.assertRaises(
exception.ResourceInError,
trunk.check_create_complete,
trunk.resource_id)
self.assertIn(
'Went to status DEGRADED due to',
six.text_type(e))
def test_create_parent_port_by_name(self):
t = template_format.parse(create_template)
t['resources']['parent_port'][
'properties']['name'] = 'parent port name'
t['resources']['trunk'][
'properties']['port'] = 'parent port name'
del t['resources']['trunk']['properties']['sub_ports']
stack = utils.parse_stack(t)
parent_port = stack['parent_port']
self.patchobject(parent_port, 'get_reference_id',
return_value='parent port id')
stk_defn.update_resource_data(stack.defn, parent_port.name,
parent_port.node_data())
def find_resourceid_by_name_or_id(
_client, _resource, name_or_id, **_kwargs):
name_to_id = {
'parent port name': 'parent port id',
'parent port id': 'parent port id',
}
return name_to_id[name_or_id]
self.find_resource_mock.side_effect = find_resourceid_by_name_or_id
self._create_trunk(stack)
self.create_trunk_mock.assert_called_once_with({
'trunk': {
'description': 'trunk description',
'name': 'trunk name',
'port_id': 'parent port id',
}}
)
def test_create_subport_by_name(self):
t = template_format.parse(create_template)
del t['resources']['trunk']['properties']['sub_ports'][1:]
del t['resources']['subport_2']
t['resources']['subport_1'][
'properties']['name'] = 'subport name'
t['resources']['trunk'][
'properties']['sub_ports'][0]['port'] = 'subport name'
stack = utils.parse_stack(t)
parent_port = stack['parent_port']
self.patchobject(parent_port, 'get_reference_id',
return_value='parent port id')
stk_defn.update_resource_data(stack.defn, parent_port.name,
parent_port.node_data())
subport_1 = stack['subport_1']
self.patchobject(subport_1, 'get_reference_id',
return_value='subport id')
stk_defn.update_resource_data(stack.defn, subport_1.name,
subport_1.node_data())
def find_resourceid_by_name_or_id(
_client, _resource, name_or_id, **_kwargs):
name_to_id = {
'subport name': 'subport id',
'subport id': 'subport id',
'parent port name': 'parent port id',
'parent port id': 'parent port id',
}
return name_to_id[name_or_id]
self.find_resource_mock.side_effect = find_resourceid_by_name_or_id
self._create_trunk(stack)
self.create_trunk_mock.assert_called_once_with({
'trunk': {
'description': 'trunk description',
'name': 'trunk name',
'port_id': 'parent port id',
'sub_ports': [
{'port_id': 'subport id',
'segmentation_type': 'vlan',
'segmentation_id': 101},
],
}}
)
def test_delete_proper(self):
t = template_format.parse(create_template)
stack = utils.parse_stack(t)
self._create_trunk(stack)
self._delete_trunk(stack)
self.delete_trunk_mock.assert_called_once_with('trunk id')
def test_delete_already_gone(self):
t = template_format.parse(create_template)
stack = utils.parse_stack(t)
self._create_trunk(stack)
self.delete_trunk_mock.side_effect = ncex.NeutronClientException(
status_code=404)
self._delete_trunk(stack)
self.delete_trunk_mock.assert_called_once_with('trunk id')
def test_update_basic_properties(self):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
props = copy.deepcopy(t['resources']['trunk']['properties'])
props['name'] = 'new trunk name'
rsrc_defn = rsrc_defn.freeze(properties=props)
scheduler.TaskRunner(rsrc.update, rsrc_defn)()
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.update_trunk_mock.assert_called_once_with(
'trunk id', {'trunk': {'name': 'new trunk name'}}
)
self.trunk_remove_subports_mock.assert_not_called()
self.trunk_add_subports_mock.assert_not_called()
def test_update_subport_delete(self):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
props = copy.deepcopy(t['resources']['trunk']['properties'])
del props['sub_ports'][1]
rsrc_defn = rsrc_defn.freeze(properties=props)
scheduler.TaskRunner(rsrc.update, rsrc_defn)()
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.update_trunk_mock.assert_not_called()
self.trunk_remove_subports_mock.assert_called_once_with(
'trunk id', {'sub_ports': [{'port_id': u'subport_2_id'}]}
)
self.trunk_add_subports_mock.assert_not_called()
def test_update_subport_add(self):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
props = copy.deepcopy(t['resources']['trunk']['properties'])
props['sub_ports'].append(
{'port': 'subport_3_id',
'segmentation_type': 'vlan',
'segmentation_id': 103})
rsrc_defn = rsrc_defn.freeze(properties=props)
scheduler.TaskRunner(rsrc.update, rsrc_defn)()
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.update_trunk_mock.assert_not_called()
self.trunk_remove_subports_mock.assert_not_called()
self.trunk_add_subports_mock.assert_called_once_with(
'trunk id',
{'sub_ports': [
{'port_id': 'subport_3_id',
'segmentation_id': 103,
'segmentation_type': 'vlan'}
]}
)
def test_update_subport_change(self):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
props = copy.deepcopy(t['resources']['trunk']['properties'])
props['sub_ports'][1]['segmentation_id'] = 103
rsrc_defn = rsrc_defn.freeze(properties=props)
scheduler.TaskRunner(rsrc.update, rsrc_defn)()
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.update_trunk_mock.assert_not_called()
self.trunk_remove_subports_mock.assert_called_once_with(
'trunk id', {'sub_ports': [{'port_id': u'subport_2_id'}]}
)
self.trunk_add_subports_mock.assert_called_once_with(
'trunk id',
{'sub_ports': [
{'port_id': 'subport_2_id',
'segmentation_id': 103,
'segmentation_type': 'vlan'}
]}
)