Merge "Add update_reservation to floating IP plugin"
This commit is contained in:
commit
d4c6ecf885
@ -466,6 +466,11 @@ def required_fip_destroy(required_fip_id):
|
||||
return IMPL.required_fip_destroy(required_fip_id)
|
||||
|
||||
|
||||
def required_fip_destroy_by_fip_reservation_id(fip_reservation_id):
|
||||
"""Delete all required FIPs for a floating IP reservation."""
|
||||
return IMPL.required_fip_destroy_by_fip_reservation_id(fip_reservation_id)
|
||||
|
||||
|
||||
# FloatingIP Allocation
|
||||
|
||||
def fip_allocation_create(allocation_values):
|
||||
|
@ -934,6 +934,16 @@ def required_fip_destroy(required_fip_id):
|
||||
session.delete(required_fip)
|
||||
|
||||
|
||||
def required_fip_destroy_by_fip_reservation_id(fip_reservation_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
required_fips = model_query(
|
||||
models.RequiredFloatingIP, session).filter_by(
|
||||
floatingip_reservation_id=fip_reservation_id)
|
||||
for required_fip in required_fips:
|
||||
required_fip_destroy(required_fip['id'])
|
||||
|
||||
|
||||
# FloatingIP Allocation
|
||||
|
||||
def _fip_allocation_get(session, fip_allocation_id):
|
||||
|
@ -216,3 +216,9 @@ class TooLongFloatingIPs(exceptions.InvalidInput):
|
||||
|
||||
class NotEnoughFloatingIPAvailable(exceptions.InvalidInput):
|
||||
msg_fmt = _("Not enough floating IPs available")
|
||||
|
||||
|
||||
class CantUpdateFloatingIPReservation(exceptions.BlazarException):
|
||||
code = 400
|
||||
msg_fmt = _("Floating IP reservation cannot be updated with requested "
|
||||
"parameters. %(msg)s")
|
||||
|
@ -19,8 +19,6 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from blazar.db import api as db_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -66,12 +64,10 @@ class BasePlugin(object):
|
||||
"""Reserve resource."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_reservation(self, reservation_id, values):
|
||||
"""Update reservation."""
|
||||
reservation_values = {
|
||||
'resource_id': values['resource_id']
|
||||
}
|
||||
db_api.reservation_update(reservation_id, reservation_values)
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def on_end(self, resource_id):
|
||||
|
@ -27,6 +27,7 @@ from blazar import exceptions
|
||||
from blazar.manager import exceptions as manager_ex
|
||||
from blazar.plugins import base
|
||||
from blazar.plugins import floatingips as plugin
|
||||
from blazar import status
|
||||
from blazar.utils.openstack import neutron
|
||||
from blazar.utils import plugins as plugins_utils
|
||||
|
||||
@ -61,6 +62,84 @@ class FloatingIpPlugin(base.BasePlugin):
|
||||
if not (netutils.is_valid_ipv4(ip) or netutils.is_valid_ipv6(ip)):
|
||||
raise manager_ex.InvalidIPFormat(ip=ip)
|
||||
|
||||
def _update_allocations(self, dates_before, dates_after, reservation_id,
|
||||
reservation_status, fip_reservation, values):
|
||||
amount = int(values.get('amount', fip_reservation['amount']))
|
||||
fip_allocations = db_api.fip_allocation_get_all_by_values(
|
||||
reservation_id=reservation_id)
|
||||
allocs_to_remove = self._allocations_to_remove(
|
||||
dates_before, dates_after, fip_allocations, amount)
|
||||
|
||||
if (allocs_to_remove and
|
||||
reservation_status == status.reservation.ACTIVE):
|
||||
raise manager_ex.CantUpdateFloatingIPReservation(
|
||||
msg="Cannot remove allocations from an active reservation")
|
||||
|
||||
kept_fips = len(fip_allocations) - len(allocs_to_remove)
|
||||
fip_ids_to_add = []
|
||||
|
||||
if kept_fips < amount:
|
||||
needed_fips = amount - kept_fips
|
||||
required_fips = values.get(
|
||||
'required_floatingips',
|
||||
fip_reservation['required_floatingips'])
|
||||
fip_ids_to_add = self._matching_fips(
|
||||
fip_reservation['network_id'], required_fips, needed_fips,
|
||||
dates_after['start_date'], dates_after['end_date'])
|
||||
|
||||
if len(fip_ids_to_add) < needed_fips:
|
||||
raise manager_ex.NotEnoughFloatingIPAvailable()
|
||||
|
||||
for fip_id in fip_ids_to_add:
|
||||
LOG.debug('Adding floating IP {} to reservation {}'.format(
|
||||
fip_id, reservation_id))
|
||||
db_api.fip_allocation_create({
|
||||
'floatingip_id': fip_id,
|
||||
'reservation_id': reservation_id})
|
||||
|
||||
for allocation in allocs_to_remove:
|
||||
LOG.debug('Removing floating IP {} from reservation {}'.format(
|
||||
allocation['floatingip_id'], reservation_id))
|
||||
db_api.fip_allocation_destroy(allocation['id'])
|
||||
|
||||
def _allocations_to_remove(self, dates_before, dates_after, allocs,
|
||||
amount):
|
||||
"""Find candidate floating IP allocations to remove."""
|
||||
allocs_to_remove = []
|
||||
|
||||
for alloc in allocs:
|
||||
is_extension = (
|
||||
dates_before['start_date'] > dates_after['start_date'] or
|
||||
dates_before['end_date'] < dates_after['end_date'])
|
||||
|
||||
if is_extension:
|
||||
reserved_periods = db_utils.get_reserved_periods(
|
||||
alloc['floatingip_id'],
|
||||
dates_after['start_date'],
|
||||
dates_after['end_date'],
|
||||
datetime.timedelta(seconds=1),
|
||||
resource_type='floatingip')
|
||||
|
||||
max_start = max(dates_before['start_date'],
|
||||
dates_after['start_date'])
|
||||
min_end = min(dates_before['end_date'],
|
||||
dates_after['end_date'])
|
||||
|
||||
if not (len(reserved_periods) == 0 or
|
||||
(len(reserved_periods) == 1 and
|
||||
reserved_periods[0][0] == max_start and
|
||||
reserved_periods[0][1] == min_end)):
|
||||
allocs_to_remove.append(alloc)
|
||||
continue
|
||||
|
||||
allocs_to_keep = [a for a in allocs if a not in allocs_to_remove]
|
||||
|
||||
if len(allocs_to_keep) > amount:
|
||||
allocs_to_remove.extend(
|
||||
allocs_to_keep[:(len(allocs_to_keep) - amount)])
|
||||
|
||||
return allocs_to_remove
|
||||
|
||||
def reserve_resource(self, reservation_id, values):
|
||||
"""Create floating IP reservation."""
|
||||
self.check_params(values)
|
||||
@ -95,6 +174,50 @@ class FloatingIpPlugin(base.BasePlugin):
|
||||
'reservation_id': reservation_id})
|
||||
return fip_reservation['id']
|
||||
|
||||
def update_reservation(self, reservation_id, values):
|
||||
"""Update reservation."""
|
||||
reservation = db_api.reservation_get(reservation_id)
|
||||
lease = db_api.lease_get(reservation['lease_id'])
|
||||
dates_before = {'start_date': lease['start_date'],
|
||||
'end_date': lease['end_date']}
|
||||
dates_after = {'start_date': values['start_date'],
|
||||
'end_date': values['end_date']}
|
||||
fip_reservation = db_api.fip_reservation_get(
|
||||
reservation['resource_id'])
|
||||
|
||||
if ('network_id' in values and
|
||||
values.get('network_id') != fip_reservation['network_id']):
|
||||
raise manager_ex.CantUpdateFloatingIPReservation(
|
||||
msg="Updating network_id is not supported")
|
||||
|
||||
required_fips = fip_reservation['required_floatingips']
|
||||
if ('required_floatingips' in values and
|
||||
values['required_floatingips'] != required_fips and
|
||||
values['required_floatingips'] != []):
|
||||
raise manager_ex.CantUpdateFloatingIPReservation(
|
||||
msg="Updating required_floatingips is not supported except "
|
||||
"with an empty list")
|
||||
|
||||
self._update_allocations(dates_before, dates_after, reservation_id,
|
||||
reservation['status'], fip_reservation,
|
||||
values)
|
||||
updates = {}
|
||||
if 'amount' in values:
|
||||
updates['amount'] = values.get('amount')
|
||||
if updates:
|
||||
db_api.fip_reservation_update(fip_reservation['id'], updates)
|
||||
|
||||
if ('required_floatingips' in values and
|
||||
values['required_floatingips'] != required_fips):
|
||||
db_api.required_fip_destroy_by_fip_reservation_id(
|
||||
fip_reservation['id'])
|
||||
for fip_address in values.get('required_floatingips'):
|
||||
fip_address_values = {
|
||||
'address': fip_address,
|
||||
'floatingip_reservation_id': fip_reservation['id']
|
||||
}
|
||||
db_api.required_fip_create(fip_address_values)
|
||||
|
||||
def on_start(self, resource_id):
|
||||
fip_reservation = db_api.fip_reservation_get(resource_id)
|
||||
allocations = db_api.fip_allocation_get_all_by_values(
|
||||
|
@ -54,6 +54,9 @@ class FakePlugin(base.BasePlugin):
|
||||
def reserve_resource(self, reservation_id, values):
|
||||
return None
|
||||
|
||||
def update_reservation(self, reservation_id, values):
|
||||
return None
|
||||
|
||||
def on_start(self, resource_id):
|
||||
return 'Resource %s should be started this moment.' % resource_id
|
||||
|
||||
|
@ -320,6 +320,265 @@ class FloatingIpPluginTest(tests.TestCase):
|
||||
u'441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
values)
|
||||
|
||||
def test_update_reservation_increase_amount_fips_available(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'amount': 2,
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_allocation_get_all_by_values = self.patch(
|
||||
self.db_api, 'fip_allocation_get_all_by_values'
|
||||
)
|
||||
fip_allocation_get_all_by_values.return_value = [{
|
||||
'floatingip_id': 'fip1'
|
||||
}]
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 1,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
'required_floatingips': [],
|
||||
}
|
||||
matching_fips = self.patch(fip_plugin, '_matching_fips')
|
||||
matching_fips.return_value = ['fip2']
|
||||
fip_reservation_update = self.patch(self.db_api,
|
||||
'fip_reservation_update')
|
||||
fip_allocation_create = self.patch(
|
||||
self.db_api, 'fip_allocation_create')
|
||||
fip_plugin.update_reservation(
|
||||
u'441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
values)
|
||||
fip_reservation_update.assert_called_once_with(
|
||||
'fip_resv_id1', {'amount': 2})
|
||||
calls = [
|
||||
mock.call(
|
||||
{'floatingip_id': 'fip2',
|
||||
'reservation_id': u'441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
})
|
||||
]
|
||||
fip_allocation_create.assert_has_calls(calls)
|
||||
|
||||
def test_update_reservation_increase_amount_fips_unavailable(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'amount': 2,
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_allocation_get_all_by_values = self.patch(
|
||||
self.db_api, 'fip_allocation_get_all_by_values'
|
||||
)
|
||||
fip_allocation_get_all_by_values.return_value = [{
|
||||
'floatingip_id': 'fip1'
|
||||
}]
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 1,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
'required_floatingips': [],
|
||||
}
|
||||
matching_fips = self.patch(fip_plugin, '_matching_fips')
|
||||
matching_fips.return_value = []
|
||||
self.assertRaises(mgr_exceptions.NotEnoughFloatingIPAvailable,
|
||||
fip_plugin.update_reservation,
|
||||
'441c1476-9f8f-4700-9f30-cd9b6fef3509', values)
|
||||
|
||||
def test_update_reservation_decrease_amount(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'amount': 1,
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_allocation_get_all_by_values = self.patch(
|
||||
self.db_api, 'fip_allocation_get_all_by_values'
|
||||
)
|
||||
fip_allocation_get_all_by_values.return_value = [
|
||||
{'id': 'fip_alloc_1', 'floatingip_id': 'fip1'},
|
||||
{'id': 'fip_alloc_2', 'floatingip_id': 'fip2'},
|
||||
]
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 2,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
'required_floatingips': [],
|
||||
}
|
||||
fip_allocation_destroy = self.patch(self.db_api,
|
||||
'fip_allocation_destroy')
|
||||
fip_reservation_update = self.patch(self.db_api,
|
||||
'fip_reservation_update')
|
||||
fip_plugin.update_reservation(
|
||||
u'441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
values)
|
||||
fip_reservation_update.assert_called_once_with(
|
||||
'fip_resv_id1', {'amount': 1})
|
||||
calls = [
|
||||
mock.call('fip_alloc_1')
|
||||
]
|
||||
fip_allocation_destroy.assert_has_calls(calls)
|
||||
|
||||
def test_update_reservation_remove_required_fips(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'required_floatingips': [],
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_allocation_get_all_by_values = self.patch(
|
||||
self.db_api, 'fip_allocation_get_all_by_values'
|
||||
)
|
||||
fip_allocation_get_all_by_values.return_value = [{
|
||||
'floatingip_id': 'fip1'
|
||||
}]
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 1,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
'required_floatingips': ['172.24.4.100']
|
||||
}
|
||||
required_fip_destroy_by_fip_reservation_id = self.patch(
|
||||
self.db_api, 'required_fip_destroy_by_fip_reservation_id')
|
||||
fip_plugin.update_reservation(
|
||||
u'441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
values)
|
||||
calls = [mock.call('fip_resv_id1')]
|
||||
required_fip_destroy_by_fip_reservation_id.assert_has_calls(calls)
|
||||
|
||||
def test_update_reservation_change_required_fips(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'required_floatingips': ['172.24.4.101'],
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 1,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
'required_floatingips': ['172.24.4.100']
|
||||
}
|
||||
self.assertRaises(mgr_exceptions.CantUpdateFloatingIPReservation,
|
||||
fip_plugin.update_reservation,
|
||||
'441c1476-9f8f-4700-9f30-cd9b6fef3509', values)
|
||||
|
||||
def test_update_reservation_change_network_id(self):
|
||||
fip_plugin = floatingip_plugin.FloatingIpPlugin()
|
||||
values = {
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
'resource_type': plugin.RESOURCE_TYPE,
|
||||
'network_id': 'new-network-id',
|
||||
}
|
||||
lease_get = self.patch(self.db_api, 'lease_get')
|
||||
lease_get.return_value = {
|
||||
'id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'start_date': datetime.datetime(2013, 12, 19, 20, 0),
|
||||
'end_date': datetime.datetime(2013, 12, 19, 21, 0),
|
||||
}
|
||||
reservation_get = self.patch(self.db_api, 'reservation_get')
|
||||
reservation_get.return_value = {
|
||||
'id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'lease_id': '018c1b43-e69e-4aef-a543-09681539cf4c',
|
||||
'resource_id': 'fip-reservation-id-1',
|
||||
'resource_type': 'virtual:floatingip',
|
||||
'status': 'pending',
|
||||
}
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
'id': 'fip_resv_id1',
|
||||
'amount': 1,
|
||||
'reservation_id': '441c1476-9f8f-4700-9f30-cd9b6fef3509',
|
||||
'network_id': 'f548089e-fb3e-4013-a043-c5ed809c7a67',
|
||||
}
|
||||
self.assertRaises(mgr_exceptions.CantUpdateFloatingIPReservation,
|
||||
fip_plugin.update_reservation,
|
||||
'441c1476-9f8f-4700-9f30-cd9b6fef3509', values)
|
||||
|
||||
def test_on_start(self):
|
||||
fip_reservation_get = self.patch(self.db_api, 'fip_reservation_get')
|
||||
fip_reservation_get.return_value = {
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for updating floating IP reservations, with some limitations.
|
||||
The ``amount`` of reserved floating IPs can be updated; however, updating
|
||||
``network_id`` is denied and ``required_floatingips`` can only be changed
|
||||
to an empty list.
|
Loading…
Reference in New Issue
Block a user