New APIs as per blueprint interface-api-cleanup
Also defines the 1.0 API that doesnt contain the old apis of 0.1 that we plan to remove Change-Id: I3aed4fcfefc07af7026f94ae62d4e842d6145007
This commit is contained in:
parent
ddca783309
commit
fe4b075c15
@ -63,13 +63,17 @@ notifier_queue_transport = memory
|
|||||||
[composite:melange]
|
[composite:melange]
|
||||||
use = call:melange.common.wsgi:versioned_urlmap
|
use = call:melange.common.wsgi:versioned_urlmap
|
||||||
/: versions
|
/: versions
|
||||||
/v0.1: melangeapi
|
/v0.1: melangeapp_v0_1
|
||||||
|
/v1.0: melangeapp_v1_0
|
||||||
|
|
||||||
[app:versions]
|
[app:versions]
|
||||||
paste.app_factory = melange.versions:app_factory
|
paste.app_factory = melange.versions:app_factory
|
||||||
|
|
||||||
[pipeline:melangeapi]
|
[pipeline:melangeapi_v0_1]
|
||||||
pipeline = extensions melangeapp
|
pipeline = extensions melangeapp_v0_1
|
||||||
|
|
||||||
|
[pipeline:melangeapi_v1_0]
|
||||||
|
pipeline = extensions melangeapp_v1_0
|
||||||
|
|
||||||
[filter:extensions]
|
[filter:extensions]
|
||||||
paste.filter_factory = melange.common.extensions:factory
|
paste.filter_factory = melange.common.extensions:factory
|
||||||
@ -87,8 +91,11 @@ admin_token = 999888777666
|
|||||||
[filter:authorization]
|
[filter:authorization]
|
||||||
paste.filter_factory = melange.common.auth:AuthorizationMiddleware.factory
|
paste.filter_factory = melange.common.auth:AuthorizationMiddleware.factory
|
||||||
|
|
||||||
[app:melangeapp]
|
[app:melangeapp_v0_1]
|
||||||
paste.app_factory = melange.ipam.service:app_factory
|
paste.app_factory = melange.ipam.service:APIV01.app_factory
|
||||||
|
|
||||||
|
[app:melangeapp_v1_0]
|
||||||
|
paste.app_factory = melange.ipam.service:APIV10.app_factory
|
||||||
|
|
||||||
#Add this filter to log request and response for debugging
|
#Add this filter to log request and response for debugging
|
||||||
[filter:debug]
|
[filter:debug]
|
||||||
|
@ -810,6 +810,20 @@ class Interface(ModelBase):
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
mac_address)
|
mac_address)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_and_allocate_ips(cls,
|
||||||
|
device_id=None,
|
||||||
|
network_params=None,
|
||||||
|
**kwargs):
|
||||||
|
interface = Interface.create_and_configure(device_id=device_id,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
if network_params:
|
||||||
|
network = Network.find_or_create_by(network_params.pop('id'),
|
||||||
|
network_params.pop('tenant_id'))
|
||||||
|
network.allocate_ips(interface=interface, **network_params)
|
||||||
|
return interface
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_and_configure(cls, virtual_interface_id=None, device_id=None,
|
def create_and_configure(cls, virtual_interface_id=None, device_id=None,
|
||||||
tenant_id=None, mac_address=None):
|
tenant_id=None, mac_address=None):
|
||||||
|
@ -439,7 +439,7 @@ class InterfacesController(BaseController, ShowAction, DeleteAction):
|
|||||||
|
|
||||||
class InstanceInterfacesController(BaseController):
|
class InstanceInterfacesController(BaseController):
|
||||||
|
|
||||||
def update(self, request, device_id, body=None):
|
def update_all(self, request, device_id, body=None):
|
||||||
models.Interface.delete_by(device_id=device_id)
|
models.Interface.delete_by(device_id=device_id)
|
||||||
|
|
||||||
params = self._extract_required_params(body, 'instance')
|
params = self._extract_required_params(body, 'instance')
|
||||||
@ -448,31 +448,77 @@ class InstanceInterfacesController(BaseController):
|
|||||||
for iface in params['interfaces']:
|
for iface in params['interfaces']:
|
||||||
|
|
||||||
network_params = utils.stringify_keys(iface.pop('network', None))
|
network_params = utils.stringify_keys(iface.pop('network', None))
|
||||||
interface = models.Interface.create_and_configure(
|
interface = models.Interface.create_and_allocate_ips(
|
||||||
device_id=device_id, tenant_id=tenant_id, **iface)
|
device_id=device_id,
|
||||||
|
network_params=network_params,
|
||||||
if network_params:
|
tenant_id=tenant_id,
|
||||||
network = models.Network.find_or_create_by(
|
**iface)
|
||||||
network_params.pop('id'),
|
|
||||||
network_params.pop('tenant_id'))
|
|
||||||
network.allocate_ips(interface=interface, **network_params)
|
|
||||||
|
|
||||||
view_data = views.InterfaceConfigurationView(interface).data()
|
view_data = views.InterfaceConfigurationView(interface).data()
|
||||||
created_interfaces.append(view_data)
|
created_interfaces.append(view_data)
|
||||||
|
|
||||||
return {'instance': {'interfaces': created_interfaces}}
|
return {'instance': {'interfaces': created_interfaces}}
|
||||||
|
|
||||||
def show(self, request, device_id):
|
def index(self, request, device_id):
|
||||||
interfaces = models.Interface.find_all(device_id=device_id)
|
interfaces = models.Interface.find_all(device_id=device_id)
|
||||||
view_data = [views.InterfaceConfigurationView(iface).data()
|
view_data = [views.InterfaceConfigurationView(iface).data()
|
||||||
for iface in interfaces]
|
for iface in interfaces]
|
||||||
|
|
||||||
return {'instance': {'interfaces': view_data}}
|
return {'instance': {'interfaces': view_data}}
|
||||||
|
|
||||||
def delete(self, request, device_id):
|
def delete_all(self, request, device_id):
|
||||||
LOG.debug("Deleting instance interface (device_id=%s)" % device_id)
|
LOG.debug("Deleting instance interface (device_id=%s)" % device_id)
|
||||||
models.Interface.delete_by(device_id=device_id)
|
models.Interface.delete_by(device_id=device_id)
|
||||||
|
|
||||||
|
def create(self, request, device_id, body=None):
|
||||||
|
iface_params = self._extract_required_params(body, 'interface')
|
||||||
|
network_params = utils.stringify_keys(iface_params.pop('network',
|
||||||
|
None))
|
||||||
|
interface = models.Interface.create_and_allocate_ips(
|
||||||
|
device_id=device_id,
|
||||||
|
network_params=network_params,
|
||||||
|
**iface_params)
|
||||||
|
view_data = views.InterfaceConfigurationView(interface).data()
|
||||||
|
return dict(interface=view_data)
|
||||||
|
|
||||||
|
def show(self, request, id, device_id, tenant_id=None):
|
||||||
|
iface_params = dict(device_id=device_id, id=id)
|
||||||
|
if tenant_id:
|
||||||
|
iface_params.update(dict(tenant_id=tenant_id))
|
||||||
|
|
||||||
|
interface = models.Interface.find_by(**iface_params)
|
||||||
|
view_data = views.InterfaceConfigurationView(interface).data()
|
||||||
|
return dict(interface=view_data)
|
||||||
|
|
||||||
|
def delete(self, request, id, device_id):
|
||||||
|
interface = models.Interface.find_by(id=id, device_id=device_id)
|
||||||
|
interface.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceInterfaceIpsController(BaseController):
|
||||||
|
|
||||||
|
def create(self, request, body, device_id, interface_id):
|
||||||
|
params = self._extract_required_params(body, 'network')
|
||||||
|
interface = models.Interface.find_by(id=interface_id,
|
||||||
|
device_id=device_id)
|
||||||
|
network = models.Network.find_by(params.pop('id'),
|
||||||
|
tenant_id=params.pop('tenant_id'))
|
||||||
|
ips = network.allocate_ips(interface=interface, **params)
|
||||||
|
ip_config_view = views.IpConfigurationView(*ips)
|
||||||
|
return wsgi.Result(dict(ip_addresses=ip_config_view.data()), 201)
|
||||||
|
|
||||||
|
def delete(self, request, device_id, interface_id, address):
|
||||||
|
interface = models.Interface.find_by(device_id=device_id,
|
||||||
|
id=interface_id)
|
||||||
|
network_id = interface.plugged_in_network_id()
|
||||||
|
if not network_id:
|
||||||
|
raise models.ModelNotFoundError(_("IpAddress Not Found"))
|
||||||
|
|
||||||
|
network = models.Network.find_by(network_id)
|
||||||
|
ip = network.find_allocated_ip(interface_id=interface.id,
|
||||||
|
address=address)
|
||||||
|
ip.deallocate()
|
||||||
|
|
||||||
|
|
||||||
class MacAddressRangesController(BaseController, ShowAction, DeleteAction):
|
class MacAddressRangesController(BaseController, ShowAction, DeleteAction):
|
||||||
|
|
||||||
@ -522,63 +568,54 @@ class InterfaceAllowedIpsController(BaseController):
|
|||||||
interface.disallow_ip(ip)
|
interface.disallow_ip(ip)
|
||||||
|
|
||||||
|
|
||||||
class API(wsgi.Router):
|
class APIV01(wsgi.Router):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
mapper = routes.Mapper()
|
mapper = routes.Mapper()
|
||||||
super(API, self).__init__(mapper)
|
super(APIV01, self).__init__(mapper)
|
||||||
self._natting_mapper(mapper,
|
|
||||||
"inside_globals",
|
|
||||||
InsideGlobalsController().create_resource())
|
|
||||||
self._natting_mapper(mapper,
|
|
||||||
"inside_locals",
|
|
||||||
InsideLocalsController().create_resource())
|
|
||||||
self._block_and_nested_resource_mapper(mapper)
|
|
||||||
self._policy_and_rules_mapper(mapper)
|
|
||||||
self._networks_maper(mapper)
|
self._networks_maper(mapper)
|
||||||
self._interface_ip_allocations_mapper(mapper)
|
self._interface_ip_allocations_mapper(mapper)
|
||||||
self._allocated_ips_mapper(mapper)
|
|
||||||
self._ip_routes_mapper(mapper)
|
|
||||||
self._interface_mapper(mapper)
|
self._interface_mapper(mapper)
|
||||||
self._instance_interface_mapper(mapper)
|
self._allowed_ips_mapper(mapper)
|
||||||
self._mac_address_range_mapper(mapper)
|
APICommon(mapper)
|
||||||
|
|
||||||
def _allocated_ips_mapper(self, mapper):
|
def _networks_maper(self, mapper):
|
||||||
allocated_ips_res = AllocatedIpAddressesController().create_resource()
|
resource = NetworksController().create_resource()
|
||||||
self._connect(mapper,
|
path = "/ipam/tenants/{tenant_id}/networks/{network_id}"
|
||||||
"/ipam/allocated_ip_addresses",
|
mapper.resource("networks", path, controller=resource)
|
||||||
controller=allocated_ips_res,
|
|
||||||
action="index",
|
|
||||||
conditions=dict(method=['GET']))
|
|
||||||
self._connect(mapper,
|
|
||||||
"/ipam/tenants/{tenant_id}/allocated_ip_addresses",
|
|
||||||
controller=allocated_ips_res,
|
|
||||||
action="index",
|
|
||||||
conditions=dict(method=['GET']))
|
|
||||||
|
|
||||||
def _ip_routes_mapper(self, mapper):
|
def _interface_ip_allocations_mapper(self, mapper):
|
||||||
ip_routes_res = IpRoutesController().create_resource()
|
path = ("/ipam/tenants/{tenant_id}/networks"
|
||||||
path = ("/ipam/tenants/{tenant_id}/ip_blocks/{source_block_id}"
|
"/{network_id}/interfaces/{interface_id}")
|
||||||
"/ip_routes")
|
resource = InterfaceIpAllocationsController().create_resource()
|
||||||
mapper.resource("ip_routes", path, controller=ip_routes_res)
|
with mapper.submapper(controller=resource, path_prefix=path) as submap:
|
||||||
|
_connect(submap, "/ip_allocations", action='create',
|
||||||
|
conditions=dict(method=['POST']))
|
||||||
|
_connect(submap,
|
||||||
|
"/ip_allocations",
|
||||||
|
action='index',
|
||||||
|
conditions=dict(method=['GET']))
|
||||||
|
_connect(submap, "/ip_allocations", action='bulk_delete',
|
||||||
|
conditions=dict(method=['DELETE']))
|
||||||
|
|
||||||
def _interface_mapper(self, mapper):
|
def _interface_mapper(self, mapper):
|
||||||
interface_res = InterfacesController().create_resource()
|
interface_res = InterfacesController().create_resource()
|
||||||
interface_allowed_ips = InterfaceAllowedIpsController()
|
|
||||||
path = "/ipam/interfaces"
|
path = "/ipam/interfaces"
|
||||||
self._connect(mapper,
|
_connect(mapper,
|
||||||
"/ipam/tenants/{tenant_id}/"
|
"/ipam/tenants/{tenant_id}/"
|
||||||
"interfaces/{virtual_interface_id}",
|
"interfaces/{virtual_interface_id}",
|
||||||
controller=interface_res,
|
controller=interface_res,
|
||||||
action="show",
|
action="show",
|
||||||
conditions=dict(method=['GET']))
|
conditions=dict(method=['GET']))
|
||||||
self._connect(mapper,
|
_connect(mapper,
|
||||||
"/ipam/interfaces/{virtual_interface_id}",
|
"/ipam/interfaces/{virtual_interface_id}",
|
||||||
controller=interface_res,
|
controller=interface_res,
|
||||||
action="delete",
|
action="delete",
|
||||||
conditions=dict(method=['DELETE']))
|
conditions=dict(method=['DELETE']))
|
||||||
mapper.resource("interfaces", path, controller=interface_res)
|
mapper.resource("interfaces", path, controller=interface_res)
|
||||||
|
|
||||||
|
def _allowed_ips_mapper(self, mapper):
|
||||||
|
interface_allowed_ips = InterfaceAllowedIpsController()
|
||||||
mapper.connect("/ipam/tenants/{tenant_id}/"
|
mapper.connect("/ipam/tenants/{tenant_id}/"
|
||||||
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
|
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
|
||||||
action="delete",
|
action="delete",
|
||||||
@ -595,48 +632,106 @@ class API(wsgi.Router):
|
|||||||
path_prefix=("/ipam/tenants/{tenant_id}/"
|
path_prefix=("/ipam/tenants/{tenant_id}/"
|
||||||
"interfaces/{interface_id}"))
|
"interfaces/{interface_id}"))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def app_factory(cls, global_conf, **local_conf):
|
||||||
|
return APIV01()
|
||||||
|
|
||||||
|
|
||||||
|
class APIV10(wsgi.Router):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
mapper = routes.Mapper()
|
||||||
|
super(APIV10, self).__init__(mapper)
|
||||||
|
APICommon(mapper)
|
||||||
|
self._instance_interface_ips_mapper(mapper)
|
||||||
|
|
||||||
|
def _instance_interface_ips_mapper(self, mapper):
|
||||||
|
res = InstanceInterfaceIpsController().create_resource()
|
||||||
|
path_prefix = ("/ipam/instances/{device_id}/"
|
||||||
|
"interfaces/{interface_id}")
|
||||||
|
with mapper.submapper(controller=res,
|
||||||
|
path_prefix=path_prefix) as submap:
|
||||||
|
_connect(submap,
|
||||||
|
"/ip_addresses/{address:.+?}",
|
||||||
|
action="delete",
|
||||||
|
conditions=dict(method=["DELETE"]))
|
||||||
|
mapper.resource("ip_addresses",
|
||||||
|
"/ip_addresses",
|
||||||
|
controller=res,
|
||||||
|
path_prefix=path_prefix)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def app_factory(cls, global_conf, **local_conf):
|
||||||
|
return APIV10()
|
||||||
|
|
||||||
|
|
||||||
|
class APICommon():
|
||||||
|
|
||||||
|
def __init__(self, mapper):
|
||||||
|
self._natting_mapper(mapper,
|
||||||
|
"inside_globals",
|
||||||
|
InsideGlobalsController().create_resource())
|
||||||
|
self._natting_mapper(mapper,
|
||||||
|
"inside_locals",
|
||||||
|
InsideLocalsController().create_resource())
|
||||||
|
self._block_and_nested_resource_mapper(mapper)
|
||||||
|
self._policy_and_rules_mapper(mapper)
|
||||||
|
self._allocated_ips_mapper(mapper)
|
||||||
|
self._ip_routes_mapper(mapper)
|
||||||
|
self._instance_interface_mapper(mapper)
|
||||||
|
self._mac_address_range_mapper(mapper)
|
||||||
|
|
||||||
|
def _allocated_ips_mapper(self, mapper):
|
||||||
|
allocated_ips_res = AllocatedIpAddressesController().create_resource()
|
||||||
|
_connect(mapper,
|
||||||
|
"/ipam/allocated_ip_addresses",
|
||||||
|
controller=allocated_ips_res,
|
||||||
|
action="index",
|
||||||
|
conditions=dict(method=['GET']))
|
||||||
|
_connect(mapper,
|
||||||
|
"/ipam/tenants/{tenant_id}/allocated_ip_addresses",
|
||||||
|
controller=allocated_ips_res,
|
||||||
|
action="index",
|
||||||
|
conditions=dict(method=['GET']))
|
||||||
|
|
||||||
|
def _ip_routes_mapper(self, mapper):
|
||||||
|
ip_routes_res = IpRoutesController().create_resource()
|
||||||
|
path = ("/ipam/tenants/{tenant_id}/ip_blocks/{source_block_id}"
|
||||||
|
"/ip_routes")
|
||||||
|
mapper.resource("ip_routes", path, controller=ip_routes_res)
|
||||||
|
|
||||||
def _instance_interface_mapper(self, mapper):
|
def _instance_interface_mapper(self, mapper):
|
||||||
res = InstanceInterfacesController().create_resource()
|
res = InstanceInterfacesController().create_resource()
|
||||||
self._connect(mapper,
|
_connect(mapper,
|
||||||
"/ipam/instances/{device_id}/interfaces",
|
"/ipam/instances/{device_id}/interfaces",
|
||||||
controller=res,
|
controller=res,
|
||||||
action="update",
|
action="update_all",
|
||||||
conditions=dict(method=['PUT']))
|
conditions=dict(method=['PUT']))
|
||||||
self._connect(mapper,
|
_connect(mapper,
|
||||||
"/ipam/instances/{device_id}/interfaces",
|
"/ipam/instances/{device_id}/interfaces",
|
||||||
controller=res,
|
controller=res,
|
||||||
action="show",
|
action="index",
|
||||||
conditions=dict(method=['GET']))
|
conditions=dict(method=['GET']))
|
||||||
self._connect(mapper,
|
_connect(mapper,
|
||||||
"/ipam/instances/{device_id}/interfaces",
|
"/ipam/instances/{device_id}/interfaces",
|
||||||
controller=res,
|
controller=res,
|
||||||
action="delete",
|
action="delete_all",
|
||||||
conditions=dict(method=['DELETE']))
|
conditions=dict(method=['DELETE']))
|
||||||
|
mapper.resource("interfaces",
|
||||||
|
"/ipam/instances/{device_id}/interfaces",
|
||||||
|
controller=res)
|
||||||
|
_connect(mapper,
|
||||||
|
"/ipam/tenants/{tenant_id}/instances/{device_id}/"
|
||||||
|
"interfaces/{id}",
|
||||||
|
controller=res,
|
||||||
|
action="show",
|
||||||
|
conditions=dict(method=['GET']))
|
||||||
|
|
||||||
def _mac_address_range_mapper(self, mapper):
|
def _mac_address_range_mapper(self, mapper):
|
||||||
range_res = MacAddressRangesController().create_resource()
|
range_res = MacAddressRangesController().create_resource()
|
||||||
path = ("/ipam/mac_address_ranges")
|
path = ("/ipam/mac_address_ranges")
|
||||||
mapper.resource("mac_address_ranges", path, controller=range_res)
|
mapper.resource("mac_address_ranges", path, controller=range_res)
|
||||||
|
|
||||||
def _networks_maper(self, mapper):
|
|
||||||
resource = NetworksController().create_resource()
|
|
||||||
path = "/ipam/tenants/{tenant_id}/networks/{network_id}"
|
|
||||||
mapper.resource("networks", path, controller=resource)
|
|
||||||
|
|
||||||
def _interface_ip_allocations_mapper(self, mapper):
|
|
||||||
path = ("/ipam/tenants/{tenant_id}/networks"
|
|
||||||
"/{network_id}/interfaces/{interface_id}")
|
|
||||||
resource = InterfaceIpAllocationsController().create_resource()
|
|
||||||
with mapper.submapper(controller=resource, path_prefix=path) as submap:
|
|
||||||
self._connect(submap, "/ip_allocations", action='create',
|
|
||||||
conditions=dict(method=['POST']))
|
|
||||||
self._connect(submap,
|
|
||||||
"/ip_allocations",
|
|
||||||
action='index',
|
|
||||||
conditions=dict(method=['GET']))
|
|
||||||
self._connect(submap, "/ip_allocations", action='bulk_delete',
|
|
||||||
conditions=dict(method=['DELETE']))
|
|
||||||
|
|
||||||
def _policy_and_rules_mapper(self, mapper):
|
def _policy_and_rules_mapper(self, mapper):
|
||||||
policy_path = "/ipam/tenants/{tenant_id}/policies"
|
policy_path = "/ipam/tenants/{tenant_id}/policies"
|
||||||
ip_ranges_resource = UnusableIpRangesController().create_resource()
|
ip_ranges_resource = UnusableIpRangesController().create_resource()
|
||||||
@ -672,10 +767,10 @@ class API(wsgi.Router):
|
|||||||
parent_resource["member_name"])
|
parent_resource["member_name"])
|
||||||
with mapper.submapper(controller=subnet_controller,
|
with mapper.submapper(controller=subnet_controller,
|
||||||
path_prefix=path_prefix) as submap:
|
path_prefix=path_prefix) as submap:
|
||||||
self._connect(submap, "/subnets",
|
_connect(submap, "/subnets",
|
||||||
action="index",
|
action="index",
|
||||||
conditions=dict(method=["GET"]))
|
conditions=dict(method=["GET"]))
|
||||||
self._connect(submap, "/subnets",
|
_connect(submap, "/subnets",
|
||||||
action="create",
|
action="create",
|
||||||
conditions=dict(method=["POST"]))
|
conditions=dict(method=["POST"]))
|
||||||
|
|
||||||
@ -685,23 +780,23 @@ class API(wsgi.Router):
|
|||||||
parent_resource["member_name"])
|
parent_resource["member_name"])
|
||||||
with mapper.submapper(controller=ip_address_controller,
|
with mapper.submapper(controller=ip_address_controller,
|
||||||
path_prefix=path_prefix) as submap:
|
path_prefix=path_prefix) as submap:
|
||||||
self._connect(submap,
|
_connect(submap,
|
||||||
"/ip_addresses/{address:.+?}",
|
"/ip_addresses/{address:.+?}",
|
||||||
action="show",
|
action="show",
|
||||||
conditions=dict(method=["GET"]))
|
conditions=dict(method=["GET"]))
|
||||||
self._connect(submap,
|
_connect(submap,
|
||||||
"/ip_addresses/{address:.+?}",
|
"/ip_addresses/{address:.+?}",
|
||||||
action="delete",
|
action="delete",
|
||||||
conditions=dict(method=["DELETE"]))
|
conditions=dict(method=["DELETE"]))
|
||||||
self._connect(submap,
|
_connect(submap,
|
||||||
"/ip_addresses/{address:.+?}""/restore",
|
"/ip_addresses/{address:.+?}""/restore",
|
||||||
action="restore",
|
action="restore",
|
||||||
conditions=dict(method=["PUT"]))
|
conditions=dict(method=["PUT"]))
|
||||||
|
|
||||||
#mapper.resource here for ip addresses was slowing down the tests
|
#mapper.resource here for ip addresses was slowing down the tests
|
||||||
self._connect(submap, "/ip_addresses", action="create",
|
_connect(submap, "/ip_addresses", action="create",
|
||||||
conditions=dict(method=["POST"]))
|
conditions=dict(method=["POST"]))
|
||||||
self._connect(submap, "/ip_addresses", action="index",
|
_connect(submap, "/ip_addresses", action="index",
|
||||||
conditions=dict(method=["GET"]))
|
conditions=dict(method=["GET"]))
|
||||||
|
|
||||||
def _natting_mapper(self, mapper, nat_type, nat_controller):
|
def _natting_mapper(self, mapper, nat_type, nat_controller):
|
||||||
@ -709,20 +804,17 @@ class API(wsgi.Router):
|
|||||||
"ip_addresses/{address:.+?}/")
|
"ip_addresses/{address:.+?}/")
|
||||||
with mapper.submapper(controller=nat_controller,
|
with mapper.submapper(controller=nat_controller,
|
||||||
path_prefix=path_prefix) as submap:
|
path_prefix=path_prefix) as submap:
|
||||||
self._connect(submap, nat_type, action="create",
|
_connect(submap, nat_type, action="create",
|
||||||
conditions=dict(method=["POST"]))
|
conditions=dict(method=["POST"]))
|
||||||
self._connect(submap, nat_type, action="index",
|
_connect(submap, nat_type, action="index",
|
||||||
conditions=dict(method=["GET"]))
|
conditions=dict(method=["GET"]))
|
||||||
self._connect(submap, nat_type, action="delete",
|
_connect(submap, nat_type, action="delete",
|
||||||
conditions=dict(method=["DELETE"]))
|
conditions=dict(method=["DELETE"]))
|
||||||
self._connect(submap,
|
_connect(submap,
|
||||||
"%(nat_type)s/{%(nat_type)s_address:.+?}" % locals(),
|
"%(nat_type)s/{%(nat_type)s_address:.+?}" % locals(),
|
||||||
action="delete",
|
action="delete",
|
||||||
conditions=dict(method=["DELETE"]))
|
conditions=dict(method=["DELETE"]))
|
||||||
|
|
||||||
def _connect(self, mapper, path, *args, **kwargs):
|
|
||||||
return mapper.connect(path + "{.format:(json|xml)?}", *args, **kwargs)
|
|
||||||
|
|
||||||
|
def _connect(mapper, path, *args, **kwargs):
|
||||||
def app_factory(global_conf, **local_conf):
|
return mapper.connect(path + "{.format:(json|xml)?}", *args, **kwargs)
|
||||||
return API()
|
|
||||||
|
@ -73,6 +73,6 @@ class TestApp(webtest.TestApp):
|
|||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
options = {"config_file": tests.test_config_file()}
|
options = {"config_file": tests.test_config_file()}
|
||||||
conf = config.Config.load_paste_config("melangeapp", options, None)
|
conf = config.Config.load_paste_config("melange", options, None)
|
||||||
|
|
||||||
db_api.db_reset(conf, db_based_ip_generator, db_based_mac_generator)
|
db_api.db_reset(conf, db_based_ip_generator, db_based_mac_generator)
|
||||||
|
@ -28,8 +28,8 @@ class TestExtensions(unittest.TestCase):
|
|||||||
|
|
||||||
def test_extension_loads_with_melange_xmlns(self):
|
def test_extension_loads_with_melange_xmlns(self):
|
||||||
options = {'config_file': tests.test_config_file()}
|
options = {'config_file': tests.test_config_file()}
|
||||||
conf, app = config.Config.load_paste_app('melangeapi',
|
conf, app = config.Config.load_paste_app('melangeapi_v0_1',
|
||||||
options, None)
|
options, None)
|
||||||
test_app = webtest.TestApp(app)
|
test_app = webtest.TestApp(app)
|
||||||
|
|
||||||
response = test_app.get("/extensions.xml")
|
response = test_app.get("/extensions.xml")
|
||||||
|
@ -39,9 +39,12 @@ class ControllerTestBase(tests.BaseTest):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ControllerTestBase, self).setUp()
|
super(ControllerTestBase, self).setUp()
|
||||||
conf, melange_app = config.Config.load_paste_app('melangeapp',
|
conf, melange_v0_1 = config.Config.load_paste_app('melangeapp_v0_1',
|
||||||
{"config_file": tests.test_config_file()}, None)
|
{"config_file": tests.test_config_file()}, None)
|
||||||
self.app = unit.TestApp(melange_app)
|
self.app = unit.TestApp(melange_v0_1)
|
||||||
|
conf, melange_v1_0 = config.Config.load_paste_app('melangeapp_v1_0',
|
||||||
|
{"config_file": tests.test_config_file()}, None)
|
||||||
|
self.appv1_0 = unit.TestApp(melange_v1_0)
|
||||||
|
|
||||||
|
|
||||||
class DummyApp(wsgi.Router):
|
class DummyApp(wsgi.Router):
|
||||||
@ -49,7 +52,7 @@ class DummyApp(wsgi.Router):
|
|||||||
def __init__(self, controller):
|
def __init__(self, controller):
|
||||||
mapper = routes.Mapper()
|
mapper = routes.Mapper()
|
||||||
mapper.resource("resource", "/resources",
|
mapper.resource("resource", "/resources",
|
||||||
controller=controller.create_resource())
|
controller=controller.create_resource())
|
||||||
super(DummyApp, self).__init__(mapper)
|
super(DummyApp, self).__init__(mapper)
|
||||||
|
|
||||||
|
|
||||||
@ -2314,7 +2317,7 @@ class TestInterfacesController(ControllerTestBase):
|
|||||||
|
|
||||||
class TestInstanceInterfacesController(ControllerTestBase):
|
class TestInstanceInterfacesController(ControllerTestBase):
|
||||||
|
|
||||||
def test_update_creates_interfaces(self):
|
def test_update_all_creates_interfaces(self):
|
||||||
net_ids = ["net_id_1", "net_id_2", "net_id_3"]
|
net_ids = ["net_id_1", "net_id_2", "net_id_3"]
|
||||||
for net_id in net_ids:
|
for net_id in net_ids:
|
||||||
factory_models.PrivateIpBlockFactory(tenant_id="RAX",
|
factory_models.PrivateIpBlockFactory(tenant_id="RAX",
|
||||||
@ -2330,6 +2333,7 @@ class TestInstanceInterfacesController(ControllerTestBase):
|
|||||||
|
|
||||||
response = self.app.put_json("/ipam/instances/instance_id/interfaces",
|
response = self.app.put_json("/ipam/instances/instance_id/interfaces",
|
||||||
put_data)
|
put_data)
|
||||||
|
|
||||||
self.assertEqual(response.status_int, 200)
|
self.assertEqual(response.status_int, 200)
|
||||||
ifaces = sorted(models.Interface.find_all(device_id='instance_id'),
|
ifaces = sorted(models.Interface.find_all(device_id='instance_id'),
|
||||||
key=lambda iface: iface.plugged_in_network_id())
|
key=lambda iface: iface.plugged_in_network_id())
|
||||||
@ -2357,7 +2361,7 @@ class TestInstanceInterfacesController(ControllerTestBase):
|
|||||||
self.assertTrue(models.IpAddress.get(
|
self.assertTrue(models.IpAddress.get(
|
||||||
previous_ip.id).marked_for_deallocation)
|
previous_ip.id).marked_for_deallocation)
|
||||||
|
|
||||||
def test_get_interfaces(self):
|
def test_get_all_interfaces(self):
|
||||||
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
network_id="net_id")
|
network_id="net_id")
|
||||||
self._setup_interface_and_ip("instance_id",
|
self._setup_interface_and_ip("instance_id",
|
||||||
@ -2370,7 +2374,7 @@ class TestInstanceInterfacesController(ControllerTestBase):
|
|||||||
self.assertEqual([self._get_iface_data(iface)],
|
self.assertEqual([self._get_iface_data(iface)],
|
||||||
response.json['instance']['interfaces'])
|
response.json['instance']['interfaces'])
|
||||||
|
|
||||||
def test_delete_interfaces(self):
|
def test_delete_all_interfaces_of_instance(self):
|
||||||
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
network_id="net_id")
|
network_id="net_id")
|
||||||
self._setup_interface_and_ip("instance_id",
|
self._setup_interface_and_ip("instance_id",
|
||||||
@ -2393,6 +2397,112 @@ class TestInstanceInterfacesController(ControllerTestBase):
|
|||||||
self.assertIsNone(deleted_instance_ifaces)
|
self.assertIsNone(deleted_instance_ifaces)
|
||||||
self.assertIsNotNone(existing_instance_ifaces)
|
self.assertIsNotNone(existing_instance_ifaces)
|
||||||
|
|
||||||
|
def test_create_an_interface(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
existing_ip_on_instance = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
response = self.app.post_json("/ipam/instances/instance_id/interfaces",
|
||||||
|
{'interface': {
|
||||||
|
'tenant_id': "leasee_tenant",
|
||||||
|
'network': {
|
||||||
|
'id': "net_id",
|
||||||
|
'tenant_id': "RAX"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertIsNotNone(models.Interface.find_by(
|
||||||
|
device_id="instance_id", id=existing_ip_on_instance.interface_id))
|
||||||
|
created_interface = models.Interface.find_by(
|
||||||
|
device_id="instance_id", id=response.json['interface']['id'])
|
||||||
|
self.assertEqual(created_interface.plugged_in_network_id(), "net_id")
|
||||||
|
self.assertEqual(created_interface.tenant_id, "leasee_tenant")
|
||||||
|
self.assertEqual(response.json['interface'],
|
||||||
|
self._get_iface_data(created_interface))
|
||||||
|
|
||||||
|
def test_show_an_interface(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
allocated_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
response = self.app.get("/ipam/instances/instance_id/interfaces/%s" %
|
||||||
|
allocated_ip.interface_id)
|
||||||
|
|
||||||
|
expected_interface = models.Interface.find(allocated_ip.interface_id)
|
||||||
|
self.assertEqual(response.json['interface'],
|
||||||
|
self._get_iface_data(expected_interface))
|
||||||
|
|
||||||
|
def test_show_an_interface_raises_404_for_non_existant_interface(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
noise_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
|
||||||
|
response = self.app.get("/ipam/instances/instance_id/interfaces/"
|
||||||
|
"bad_iface_id", status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"Interface Not Found")
|
||||||
|
|
||||||
|
def test_show_an_interface_with_tenant_id(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
allocated_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
response = self.app.get("/ipam/tenants/leasee_tenant/"
|
||||||
|
"instances/instance_id/interfaces/%s" %
|
||||||
|
allocated_ip.interface_id)
|
||||||
|
|
||||||
|
expected_interface = models.Interface.find(allocated_ip.interface_id)
|
||||||
|
self.assertEqual(response.json['interface'],
|
||||||
|
self._get_iface_data(expected_interface))
|
||||||
|
|
||||||
|
def test_show_an_inteface_fails_for_wrong_tenant_id(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
allocated_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
response = self.app.get("/ipam/tenants/wrong_tenant_id/"
|
||||||
|
"instances/instance_id/interfaces/%s" %
|
||||||
|
allocated_ip.interface_id, status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response, webob.exc.HTTPNotFound,
|
||||||
|
"Interface Not Found")
|
||||||
|
|
||||||
|
def test_delete_an_interface(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
allocated_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
|
||||||
|
self.app.delete("/ipam/instances/instance_id/interfaces/%s" %
|
||||||
|
allocated_ip.interface_id)
|
||||||
|
self.assertIsNone(models.Interface.get(allocated_ip.interface_id))
|
||||||
|
self.assertTrue(
|
||||||
|
models.IpAddress.get(allocated_ip.id).marked_for_deallocation)
|
||||||
|
|
||||||
|
def test_delete_an_interface_raises_404_for_non_existant_interface(self):
|
||||||
|
provider_block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id")
|
||||||
|
noise_ip = self._setup_interface_and_ip("instance_id",
|
||||||
|
"leasee_tenant",
|
||||||
|
provider_block)
|
||||||
|
|
||||||
|
response = self.app.delete("/ipam/instances/instance_id/interfaces/"
|
||||||
|
"bad_iface_id", status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"Interface Not Found")
|
||||||
|
|
||||||
def _get_iface_data(self, iface):
|
def _get_iface_data(self, iface):
|
||||||
return unit.sanitize(views.InterfaceConfigurationView(iface).data())
|
return unit.sanitize(views.InterfaceConfigurationView(iface).data())
|
||||||
|
|
||||||
@ -2402,6 +2512,108 @@ class TestInstanceInterfacesController(ControllerTestBase):
|
|||||||
return _allocate_ip(block, interface=iface)
|
return _allocate_ip(block, interface=iface)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInstanceInterfaceIpsController(ControllerTestBase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestInstanceInterfaceIpsController, self).setUp()
|
||||||
|
self.block = factory_models.IpBlockFactory(tenant_id="RAX",
|
||||||
|
network_id="net_id",
|
||||||
|
cidr="10.1.1.1/29")
|
||||||
|
self.iface = factory_models.InterfaceFactory(device_id="instance_id",
|
||||||
|
tenant_id="leasee_tenant")
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
path = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/%s/ip_addresses" % self.iface.id)
|
||||||
|
body = {'network':
|
||||||
|
{'id': self.block.network_id,
|
||||||
|
'tenant_id': self.block.tenant_id,
|
||||||
|
'address': "10.1.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = self.appv1_0.post_json(path, body)
|
||||||
|
|
||||||
|
created_address = models.IpAddress.find_by(address="10.1.1.3")
|
||||||
|
self.assertEqual(created_address.interface_id, self.iface.id)
|
||||||
|
self.assertEqual(created_address.ip_block_id, self.block.id)
|
||||||
|
expected_ip_data = unit.sanitize(
|
||||||
|
views.IpConfigurationView(created_address).data())
|
||||||
|
self.assertEqual(response.json['ip_addresses'], expected_ip_data)
|
||||||
|
|
||||||
|
def test_create_raises_404_for_non_existant_interface(self):
|
||||||
|
path = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/bad_iface_id/ip_addresses")
|
||||||
|
body = {'network':
|
||||||
|
{'id': self.block.network_id,
|
||||||
|
'tenant_id': self.block.tenant_id,
|
||||||
|
'address': "10.1.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.appv1_0.post_json(path, body, status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"Interface Not Found")
|
||||||
|
|
||||||
|
def test_create_raises_404_for_non_existant_network(self):
|
||||||
|
path = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/%s/ip_addresses" % self.iface.id)
|
||||||
|
body = {'network':
|
||||||
|
{'id': "bad_net_id",
|
||||||
|
'tenant_id': self.block.tenant_id,
|
||||||
|
'address': "10.1.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.appv1_0.post_json(path, body, status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"Network bad_net_id not found")
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
ip = self.block.allocate_ip(interface=self.iface)
|
||||||
|
url = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/%s/ip_addresses/%s" % (self.iface.id, ip.address))
|
||||||
|
|
||||||
|
self.appv1_0.delete(url)
|
||||||
|
|
||||||
|
self.assertTrue(models.IpAddress.find(ip.id).marked_for_deallocation)
|
||||||
|
|
||||||
|
def test_delete_raises_404_for_non_existant_interface(self):
|
||||||
|
url = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/bad_iface_id/ip_addresses/10.1.1.1")
|
||||||
|
|
||||||
|
response = self.appv1_0.delete(url, status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"Interface Not Found")
|
||||||
|
|
||||||
|
def test_delete_raises_404_for_unplugged_interface(self):
|
||||||
|
url = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/%s/ip_addresses/22.22.22.22" % self.iface.id)
|
||||||
|
|
||||||
|
response = self.appv1_0.delete(url, status="*")
|
||||||
|
|
||||||
|
self.assertErrorResponse(response,
|
||||||
|
webob.exc.HTTPNotFound,
|
||||||
|
"IpAddress Not Found")
|
||||||
|
|
||||||
|
def test_delete_raises_404_for_non_existant_ip(self):
|
||||||
|
self.block.allocate_ip(interface=self.iface)
|
||||||
|
url = ("/ipam/instances/instance_id/"
|
||||||
|
"interfaces/%s/ip_addresses/22.22.22.22" % self.iface.id)
|
||||||
|
|
||||||
|
response = self.appv1_0.delete(url, status="*")
|
||||||
|
|
||||||
|
err_msg = ("IpAddress with {'address': u'22.22.22.22', "
|
||||||
|
"'interface_id': u'%s'} for network net_id not found"
|
||||||
|
% self.iface.id)
|
||||||
|
self.assertErrorResponse(response, webob.exc.HTTPNotFound, err_msg)
|
||||||
|
|
||||||
|
|
||||||
class TestMacAddressRangesController(ControllerTestBase):
|
class TestMacAddressRangesController(ControllerTestBase):
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user