Merge "Handle replace and rollback cases for Port"
This commit is contained in:
commit
87c0b337ee
@ -12,6 +12,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
@ -432,6 +433,25 @@ class Port(neutron.NeutronResource):
|
|||||||
attributes = self._show_resource()
|
attributes = self._show_resource()
|
||||||
return self.is_built(attributes)
|
return self.is_built(attributes)
|
||||||
|
|
||||||
|
def prepare_for_replace(self):
|
||||||
|
# store port fixed_ips for restoring after failed update
|
||||||
|
fixed_ips = self._show_resource().get('fixed_ips', [])
|
||||||
|
self.data_set('port_fip', jsonutils.dumps(fixed_ips))
|
||||||
|
# reset fixed_ips for this port by setting fixed_ips to []
|
||||||
|
props = {'fixed_ips': []}
|
||||||
|
self.client().update_port(self.resource_id, {'port': props})
|
||||||
|
|
||||||
|
def restore_after_rollback(self):
|
||||||
|
old_port = self.stack._backup_stack().resources.get(self.name)
|
||||||
|
fixed_ips = old_port.data().get('port_fip', [])
|
||||||
|
# restore fixed_ips for this port by setting fixed_ips to []
|
||||||
|
props = {'fixed_ips': []}
|
||||||
|
old_props = {'fixed_ips': jsonutils.loads(fixed_ips)}
|
||||||
|
# remove ip from new port
|
||||||
|
self.client().update_port(self.resource_id, {'port': props})
|
||||||
|
# restore ip for old port
|
||||||
|
self.client().update_port(old_port.resource_id, {'port': old_props})
|
||||||
|
|
||||||
|
|
||||||
def resource_mapping():
|
def resource_mapping():
|
||||||
return {
|
return {
|
||||||
|
@ -12,10 +12,12 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import mock
|
||||||
import mox
|
import mox
|
||||||
from neutronclient.common import exceptions as qe
|
from neutronclient.common import exceptions as qe
|
||||||
from neutronclient.neutron import v2_0 as neutronV20
|
from neutronclient.neutron import v2_0 as neutronV20
|
||||||
from neutronclient.v2_0 import client as neutronclient
|
from neutronclient.v2_0 import client as neutronclient
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import template_format
|
from heat.common import template_format
|
||||||
@ -717,3 +719,61 @@ class NeutronPortTest(common.HeatTestCase):
|
|||||||
self.assertEqual('direct', port.properties['binding:vnic_type'])
|
self.assertEqual('direct', port.properties['binding:vnic_type'])
|
||||||
|
|
||||||
self.m.VerifyAll()
|
self.m.VerifyAll()
|
||||||
|
|
||||||
|
def test_prepare_for_replace_port(self):
|
||||||
|
t = template_format.parse(neutron_port_template)
|
||||||
|
stack = utils.parse_stack(t)
|
||||||
|
port = stack['port']
|
||||||
|
port.resource_id = 'test_res_id'
|
||||||
|
_value = {
|
||||||
|
'fixed_ips': {
|
||||||
|
'subnet_id': 'test_subnet',
|
||||||
|
'ip_address': '42.42.42.42'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
port._show_resource = mock.Mock(return_value=_value)
|
||||||
|
port.data_set = mock.Mock()
|
||||||
|
n_client = mock.Mock()
|
||||||
|
port.client = mock.Mock(return_value=n_client)
|
||||||
|
|
||||||
|
# execute prepare_for_replace
|
||||||
|
port.prepare_for_replace()
|
||||||
|
|
||||||
|
# check, that data was stored
|
||||||
|
port.data_set.assert_called_once_with(
|
||||||
|
'port_fip', jsonutils.dumps(_value.get('fixed_ips')))
|
||||||
|
|
||||||
|
# check, that port was updated and ip was removed
|
||||||
|
expected_props = {'port': {'fixed_ips': []}}
|
||||||
|
n_client.update_port.assert_called_once_with('test_res_id',
|
||||||
|
expected_props)
|
||||||
|
|
||||||
|
def test_restore_after_rollback_port(self):
|
||||||
|
t = template_format.parse(neutron_port_template)
|
||||||
|
stack = utils.parse_stack(t)
|
||||||
|
new_port = stack['port']
|
||||||
|
new_port.resource_id = 'new_res_id'
|
||||||
|
# mock backup stack to return only one mocked old_port
|
||||||
|
old_port = mock.Mock()
|
||||||
|
new_port.stack._backup_stack = mock.Mock()
|
||||||
|
new_port.stack._backup_stack().resources.get.return_value = old_port
|
||||||
|
old_port.resource_id = 'old_res_id'
|
||||||
|
_value = {
|
||||||
|
'subnet_id': 'test_subnet',
|
||||||
|
'ip_address': '42.42.42.42'
|
||||||
|
}
|
||||||
|
old_port.data = mock.Mock(
|
||||||
|
return_value={'port_fip': jsonutils.dumps(_value)})
|
||||||
|
|
||||||
|
n_client = mock.Mock()
|
||||||
|
new_port.client = mock.Mock(return_value=n_client)
|
||||||
|
|
||||||
|
# execute prepare_for_replace
|
||||||
|
new_port.restore_after_rollback()
|
||||||
|
|
||||||
|
# check, that ports were updated: old port get ip and
|
||||||
|
# same ip was removed from old port
|
||||||
|
expected_new_props = {'port': {'fixed_ips': []}}
|
||||||
|
expected_old_props = {'port': {'fixed_ips': _value}}
|
||||||
|
n_client.update_port.has_calls(('new_res_id', expected_new_props),
|
||||||
|
('old_res_id', expected_old_props))
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from testtools import testcase
|
|
||||||
|
|
||||||
from heat_integrationtests.functional import functional_base
|
from heat_integrationtests.functional import functional_base
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +36,12 @@ resources:
|
|||||||
fixed_ips:
|
fixed_ips:
|
||||||
- subnet: {get_resource: subnet}
|
- subnet: {get_resource: subnet}
|
||||||
ip_address: 11.11.11.11
|
ip_address: 11.11.11.11
|
||||||
|
test:
|
||||||
|
depends_on: port
|
||||||
|
type: OS::Heat::TestResource
|
||||||
|
properties:
|
||||||
|
value: Test1
|
||||||
|
fail: False
|
||||||
outputs:
|
outputs:
|
||||||
port_ip:
|
port_ip:
|
||||||
value: {get_attr: [port, fixed_ips, 0, ip_address]}
|
value: {get_attr: [port, fixed_ips, 0, ip_address]}
|
||||||
@ -73,7 +77,6 @@ class UpdatePortTest(functional_base.FunctionalTestsBase):
|
|||||||
self.assertNotEqual(_ip, new_ip)
|
self.assertNotEqual(_ip, new_ip)
|
||||||
self.assertNotEqual(_id, new_id)
|
self.assertNotEqual(_id, new_id)
|
||||||
|
|
||||||
@testcase.skip('Skipped until bug #1455100 is fixed.')
|
|
||||||
def test_stack_update_replace_with_ip(self):
|
def test_stack_update_replace_with_ip(self):
|
||||||
# create with default 'mac' parameter
|
# create with default 'mac' parameter
|
||||||
stack_identifier = self.stack_create(template=test_template)
|
stack_identifier = self.stack_create(template=test_template)
|
||||||
@ -92,6 +95,62 @@ class UpdatePortTest(functional_base.FunctionalTestsBase):
|
|||||||
self.assertEqual(_ip, new_ip)
|
self.assertEqual(_ip, new_ip)
|
||||||
self.assertNotEqual(_id, new_id)
|
self.assertNotEqual(_id, new_id)
|
||||||
|
|
||||||
|
def test_stack_update_replace_with_ip_rollback(self):
|
||||||
|
# create with default 'mac' parameter
|
||||||
|
stack_identifier = self.stack_create(template=test_template)
|
||||||
|
|
||||||
|
_id, _ip = self.get_port_id_and_ip(stack_identifier)
|
||||||
|
|
||||||
|
# Update with another 'mac' parameter
|
||||||
|
parameters = {'mac': '00-00-00-00-AA-AA'}
|
||||||
|
|
||||||
|
# make test resource failing during update
|
||||||
|
fail_template = test_template.replace('fail: False',
|
||||||
|
'fail: True')
|
||||||
|
fail_template = fail_template.replace('value: Test1',
|
||||||
|
'value: Rollback')
|
||||||
|
|
||||||
|
# port should be replaced with same ip
|
||||||
|
self.update_stack(stack_identifier, fail_template,
|
||||||
|
parameters=parameters,
|
||||||
|
expected_status='ROLLBACK_COMPLETE',
|
||||||
|
disable_rollback=False)
|
||||||
|
|
||||||
|
new_id, new_ip = self.get_port_id_and_ip(stack_identifier)
|
||||||
|
# port id and ip should be the same after rollback
|
||||||
|
self.assertEqual(_ip, new_ip)
|
||||||
|
self.assertEqual(_id, new_id)
|
||||||
|
|
||||||
|
def test_stack_update_replace_with_ip_after_failed_update(self):
|
||||||
|
# create with default 'mac' parameter
|
||||||
|
stack_identifier = self.stack_create(template=test_template)
|
||||||
|
|
||||||
|
_id, _ip = self.get_port_id_and_ip(stack_identifier)
|
||||||
|
|
||||||
|
# Update with another 'mac' parameter
|
||||||
|
parameters = {'mac': '00-00-00-00-AA-AA'}
|
||||||
|
|
||||||
|
# make test resource failing during update
|
||||||
|
fail_template = test_template.replace('fail: False',
|
||||||
|
'fail: True')
|
||||||
|
fail_template = fail_template.replace('value: Test1',
|
||||||
|
'value: Rollback')
|
||||||
|
|
||||||
|
# port should be replaced with same ip
|
||||||
|
self.update_stack(stack_identifier, fail_template,
|
||||||
|
parameters=parameters,
|
||||||
|
expected_status='UPDATE_FAILED')
|
||||||
|
|
||||||
|
# port should be replaced with same ip
|
||||||
|
self.update_stack(stack_identifier, test_template,
|
||||||
|
parameters=parameters)
|
||||||
|
|
||||||
|
new_id, new_ip = self.get_port_id_and_ip(stack_identifier)
|
||||||
|
# ip should be the same, but port id should be different, because it's
|
||||||
|
# restore replace
|
||||||
|
self.assertEqual(_ip, new_ip)
|
||||||
|
self.assertNotEqual(_id, new_id)
|
||||||
|
|
||||||
def test_stack_update_in_place_remove_ip(self):
|
def test_stack_update_in_place_remove_ip(self):
|
||||||
# create with default 'mac' parameter and defined ip_address
|
# create with default 'mac' parameter and defined ip_address
|
||||||
stack_identifier = self.stack_create(template=test_template)
|
stack_identifier = self.stack_create(template=test_template)
|
||||||
|
Loading…
Reference in New Issue
Block a user