Adding network driver interface

Definition of network driver interface.  Also removed
the floating_ip attributes of VIP because they are not
needed at this point.  Also renamed net_port_id to just
port_id and subnet_id to network_id just to be a little
bit more generically clear.

Change-Id: Ic82cb2ab25fbba7dc8caa875552f4caeafb0e4af
Implements: bp/network-driver-interface
This commit is contained in:
Brandon Logan 2015-01-10 01:50:55 -06:00
parent 53529188d0
commit 9b989e2f8a
10 changed files with 304 additions and 84 deletions

View File

@ -75,21 +75,27 @@ Load Balancers
| provisioning_status | String | Physical status of a load balancer |
+---------------------+------------+------------------------------------+
|
Virtual IP
**********
The following table lists the attributes of a VIP. If only port_id is
provided then the network_id will be populated. If only network_id is
provided then a port will be created and the port_id will be returned.
If an ip_address is provided then that IP will be attempted to be
assigned to the VIP as long as port_id or network_id is provided as well.
+------------------------------------------------------------------+
| **Fully Populated VIP Object** |
+------------------------+------+----------------------------------+
| Parameters | Type | Description |
+========================+======+==================================+
| net_port_id | UUID | ``UUID`` for neutron port |
+------------------------+------+----------------------------------+
| subnet_id | UUID | ``UUID`` for subnet |
+------------------------+------+----------------------------------+
| floating_ip_id | UUID | ``UUID`` for floating IP |
+------------------------+------+----------------------------------+
| floating_ip_network_id | UUID | ``UUID`` for floating IP network |
+------------------------+------+----------------------------------+
+----------------------------------------------------------------------+
| **Fully Populated VIP Object** |
+------------------------+----------+----------------------------------+
| Parameters | Type | Description |
+========================+==========+==================================+
| ip_address | IPv(4|6) | Frontend IP of load balancer |
+------------------------+----------+----------------------------------+
| port_id | UUID | ``UUID`` for port |
| | | (equivalent to neutron port) |
+------------------------+----------+----------------------------------+
| network_id | UUID | ``UUID`` for network |
| | | (equivalent to neutron subnet) |
+------------------------+----------+----------------------------------+
List Load Balancers
*******************
@ -112,10 +118,9 @@ Retrieve a list of load balancers.
{
'id': 'uuid',
'vip': {
'net_port_id': 'uuid',
'subnet_id': 'uuid',
'floating_ip_id': 'uuid',
'floating_ip_network_id': 'uuid'
'port_id': 'uuid',
'network_id': 'uuid',
'ip_address': '192.0.2.1'
},
'tenant_id': 'uuid',
'name': 'lb_name',
@ -147,10 +152,9 @@ Retrieve details of a load balancer.
{
'id': 'uuid',
'vip':{
'net_port_id': 'uuid',
'subnet_id': 'uuid',
'floating_ip_id': 'uuid',
'floating_ip_network_id': 'uuid'
'port_id': 'uuid',
'network_id': 'uuid',
'ip_address': '192.0.2.1'
},
'tenant_id': 'uuid',
'name': 'lb_name',
@ -198,7 +202,7 @@ Create a load balancer.
{
'vip': {
'net_port_id': 'uuid'
'port_id': 'uuid'
},
'tenant_id': 'uuid',
'name': 'lb_name',
@ -211,10 +215,9 @@ Create a load balancer.
{
'id': 'uuid',
'vip':{
'net_port_id': 'uuid',
'subnet_id': 'uuid',
'floating_ip_id': 'uuid',
'floating_ip_network_id': 'uuid'
'port_id': 'uuid',
'network_id': 'uuid',
'ip_address': '192.0.2.1'
},
'tenant_id': 'uuid',
'name': 'lb_name',
@ -265,10 +268,9 @@ Modify mutable fields of a load balancer.
{
'id': 'uuid',
'vip':{
'net_port_id': 'uuid',
'subnet_id': 'uuid',
'floating_ip_id': 'uuid',
'floating_ip_network_id': 'uuid'
'port_id': 'uuid',
'network_id': 'uuid',
'ip_address': '192.0.2.1'
},
'tenant_id': 'uuid',
'name': 'lb_name',

View File

@ -20,10 +20,8 @@ from octavia.api.v1.types import base
class VIP(base.BaseType):
"""Defines the response and acceptable POST request attributes."""
ip_address = wtypes.wsattr(base.IPAddressType())
net_port_id = wtypes.wsattr(wtypes.UuidType())
subnet_id = wtypes.wsattr(wtypes.UuidType())
floating_ip_id = wtypes.wsattr(wtypes.UuidType())
floating_ip_network_id = wtypes.wsattr(wtypes.UuidType())
port_id = wtypes.wsattr(wtypes.UuidType())
network_id = wtypes.wsattr(wtypes.UuidType())
class LoadBalancerResponse(base.BaseType):

View File

@ -172,14 +172,11 @@ class LoadBalancer(BaseDataModel):
class Vip(BaseDataModel):
def __init__(self, load_balancer_id=None, ip_address=None,
net_port_id=None, subnet_id=None, floating_ip_id=None,
floating_ip_network_id=None, load_balancer=None):
network_id=None, port_id=None, load_balancer=None):
self.load_balancer_id = load_balancer_id
self.ip_address = ip_address
self.net_port_id = net_port_id
self.subnet_id = subnet_id
self.floating_ip_id = floating_ip_id
self.floating_ip_network_id = floating_ip_network_id
self.network_id = network_id
self.port_id = port_id
self.load_balancer = load_balancer

View File

@ -0,0 +1,50 @@
# Copyright 2014 Rackspace
#
# 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.
"""update vip
Revision ID: 14892634e228
Revises: 13500e2e978d
Create Date: 2015-01-10 00:53:57.798213
"""
# revision identifiers, used by Alembic.
revision = '14892634e228'
down_revision = '13500e2e978d'
from alembic import op
import sqlalchemy as sa
def upgrade():
with op.batch_alter_table(u'vip') as batch_op:
batch_op.alter_column(u'subnet_id', new_column_name=u'network_id',
existing_type=sa.String(36))
batch_op.alter_column(u'net_port_id', new_column_name=u'port_id',
existing_type=sa.String(36))
batch_op.drop_column(u'floating_ip_id')
batch_op.drop_column(u'floating_ip_network_id')
def downgrade():
with op.batch_alter_table(u'vip') as batch_op:
batch_op.add_column(sa.Column(u'floating_ip_network_id',
sa.String(36), nullable=True))
batch_op.add_column(sa.Column(u'floating_ip_id', sa.String(36),
nullable=True))
batch_op.alter_column(u'port_id', new_column_name=u'net_port_id',
existing_type=sa.String(36))
batch_op.alter_column(u'network_id', new_column_name=u'subnet_id',
existing_type=sa.String(36))

View File

@ -227,10 +227,8 @@ class Vip(base_models.BASE):
name="fk_vip_load_balancer_id"),
nullable=False, primary_key=True)
ip_address = sa.Column(sa.String(36), nullable=True)
net_port_id = sa.Column(sa.String(36), nullable=True)
subnet_id = sa.Column(sa.String(36), nullable=True)
floating_ip_id = sa.Column(sa.String(36), nullable=True)
floating_ip_network_id = sa.Column(sa.String(36), nullable=True)
port_id = sa.Column(sa.String(36), nullable=True)
network_id = sa.Column(sa.String(36), nullable=True)
load_balancer = orm.relationship("LoadBalancer", uselist=False,
backref=orm.backref("vip", uselist=False,
cascade="delete"))

View File

@ -0,0 +1,168 @@
# Copyright 2014 Rackspace
#
# 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 abc
import six
from octavia.common import exceptions
class NetworkException(exceptions.OctaviaException):
pass
class PlugVIPException(NetworkException):
pass
class UnplugVIPException(NetworkException):
pass
class AllocateVIPException(NetworkException):
pass
class DeallocateVIPException(NetworkException):
pass
class PlugNetworkException(NetworkException):
pass
class UnplugNetworkException(NetworkException):
pass
class VIPInUseException(NetworkException):
pass
class PortNotFound(NetworkException):
pass
class NetworkNotFound(NetworkException):
pass
class VIPConfigurationNotFound(NetworkException):
pass
class AmphoraNotFound(NetworkException):
pass
class PluggedVIPNotFound(NetworkException):
pass
@six.add_metaclass(abc.ABCMeta)
class AbstractNetworkDriver(object):
"""This class defines the methods for a fully functional network driver.
Implementations of this interface can expect a rollback to occur if any of
the non-nullipotent methods raise an exception.
"""
@abc.abstractmethod
def allocate_vip(self, port_id=None, network_id=None, ip_address=None):
"""Allocates a virtual ip.
Reserves it for later use as the frontend connection of a load
balancer.
:param port_id: id of port that has already been created. If this is
provided, it will be used regardless of the other
parameters.
:param network_id: if port_id is not provided, this should be
provided to create the virtual ip on this network.
:param ip_address: will attempt to allocate this specific IP address
:return: octavia.common.data_models.VIP
:raises: AllocateVIPException, PortNotFound, NetworkNotFound
"""
pass
@abc.abstractmethod
def deallocate_vip(self, vip):
"""Removes any resources that reserved this virtual ip.
:param vip: octavia.common.data_models.VIP instance
:return: None
:raises: DeallocateVIPException, VIPInUseException,
VIPConfiigurationNotFound
"""
pass
@abc.abstractmethod
def plug_vip(self, load_balancer, vip):
"""Plugs a virtual ip as the frontend connection of a load balancer.
Sets up the routing of traffic from the vip to the load balancer
and its amphorae.
:param load_balancer: octavia.common.data_models.LoadBalancer instance
:param vip: octavia.common.data_models.VIP instance
:return: octavia.common.data_models.VIP instance
:raises: PlugVIPException
"""
pass
@abc.abstractmethod
def unplug_vip(self, load_balancer, vip):
"""Unplugs a virtual ip as the frontend connection of a load balancer.
Removes the routing of traffic from the vip to the load balancer
and its amphorae.
:param load_balancer: octavia.common.data_models.LoadBalancer instance
:param vip: octavia.common.data_models.VIP instance
:return: octavia.common.data_models.VIP instance
:raises: UnplugVIPException, PluggedVIPNotFound
"""
pass
@abc.abstractmethod
def plug_network(self, amphora_id, network_id, ip_address=None):
"""Connects an existing amphora to an existing network.
:param amphora_id: id of an amphora in the compute service
:param network_id: id of a network
:param ip_address: ip address to attempt to be assigned to interface
:return: octavia.network.data_models.Interface instance
:raises: PlugNetworkException, AmphoraNotFound, NetworkNotFound
"""
@abc.abstractmethod
def unplug_network(self, amphora_id, network_id):
"""Disconnects an existing amphora from an existing network.
:param amphora_id: id of an amphora in the compute service
:param network_id: id of a network
:return: None
:raises: UnplugNetworkException, AmphoraNotFound, NetworkNotFound
"""
pass
@abc.abstractmethod
def get_plugged_networks(self, amphora_id):
"""Retrieves the current plugged networking configuration.
:param amphora_id: id of an amphora in the compute service
:return: [octavia.network.data_models.Instance]
:raises: AmphoraNotFound
"""

View File

@ -0,0 +1,25 @@
# Copyright 2014 Rackspace
#
# 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 octavia.common import data_models
class Interface(data_models.BaseDataModel):
def __init__(self, id=None, amphora_id=None, network_id=None,
ip_address=None):
self.id = id
self.amphora_id = amphora_id
self.network_id = network_id
self.ip_address = ip_address

View File

@ -55,10 +55,8 @@ class TestLoadBalancer(base.BaseAPITest):
def test_get(self):
vip = {'ip_address': '10.0.0.1',
'floating_ip_id': uuidutils.generate_uuid(),
'net_port_id': uuidutils.generate_uuid(),
'subnet_id': uuidutils.generate_uuid(),
'floating_ip_network_id': uuidutils.generate_uuid()}
'port_id': uuidutils.generate_uuid(),
'network_id': uuidutils.generate_uuid()}
lb = self.create_load_balancer(vip, name='lb1',
description='test1_desc',
enabled=False)
@ -74,10 +72,8 @@ class TestLoadBalancer(base.BaseAPITest):
def test_create_with_vip(self):
vip = {'ip_address': '10.0.0.1',
'floating_ip_id': uuidutils.generate_uuid(),
'net_port_id': uuidutils.generate_uuid(),
'subnet_id': uuidutils.generate_uuid(),
'floating_ip_network_id': uuidutils.generate_uuid()}
'network_id': uuidutils.generate_uuid(),
'port_id': uuidutils.generate_uuid()}
lb_json = {'name': 'test1', 'description': 'test1_desc',
'vip': vip, 'enabled': False}
response = self.post(self.LBS_PATH, lb_json)
@ -102,7 +98,7 @@ class TestLoadBalancer(base.BaseAPITest):
self.post(self.LBS_PATH, lb_json, status=400)
def test_create_with_nonuuid_vip_attributes(self):
lb_json = {'vip': {'floating_ip_id': 'HI'}}
lb_json = {'vip': {'network_id': 'HI'}}
self.post(self.LBS_PATH, lb_json, status=400)
def test_update(self):
@ -113,7 +109,7 @@ class TestLoadBalancer(base.BaseAPITest):
response = self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
api_lb = response.json
r_vip = api_lb.get('vip')
self.assertIsNone(r_vip.get('floating_ip_id'))
self.assertIsNone(r_vip.get('network_id'))
self.assertEqual('lb2', api_lb.get('name'))
self.assertEqual('desc1', api_lb.get('description'))
self.assertFalse(api_lb.get('enabled'))
@ -126,12 +122,12 @@ class TestLoadBalancer(base.BaseAPITest):
def test_update_with_vip(self):
lb = self.create_load_balancer({}, name='lb1', description='desc1',
enabled=False)
lb_json = {'vip': {'floating_ip_id': '1234'}}
lb_json = {'vip': {'network_id': '1234'}}
lb = self.set_lb_status(lb.get('id'))
response = self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
api_lb = response.json
r_vip = api_lb.get('vip')
self.assertIsNone(r_vip.get('floating_ip_id'))
self.assertIsNone(r_vip.get('network_id'))
self.assertEqual('lb1', api_lb.get('name'))
self.assertEqual('desc1', api_lb.get('description'))
self.assertFalse(api_lb.get('enabled'))
@ -148,7 +144,7 @@ class TestLoadBalancer(base.BaseAPITest):
def test_update_pending_create(self):
lb = self.create_load_balancer({}, name='lb1', description='desc1',
enabled=False)
lb_json = {'vip': {'floating_ip_id': '1234'}}
lb_json = {'vip': {'network_id': '1234'}}
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
def test_delete_pending_create(self):
@ -159,7 +155,7 @@ class TestLoadBalancer(base.BaseAPITest):
def test_update_pending_update(self):
lb = self.create_load_balancer({}, name='lb1', description='desc1',
enabled=False)
lb_json = {'vip': {'floating_ip_id': '1234'}}
lb_json = {'vip': {'network_id': '1234'}}
lb = self.set_lb_status(lb.get('id'))
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
@ -167,7 +163,7 @@ class TestLoadBalancer(base.BaseAPITest):
def test_delete_pending_update(self):
lb = self.create_load_balancer({}, name='lb1', description='desc1',
enabled=False)
lb_json = {'vip': {'floating_ip_id': '1234'}}
lb_json = {'vip': {'network_id': '1234'}}
lb = self.set_lb_status(lb.get('id'))
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json)
self.delete(self.LB_PATH.format(lb_id=lb.get('id')), status=409)
@ -177,7 +173,7 @@ class TestLoadBalancer(base.BaseAPITest):
enabled=False)
lb = self.set_lb_status(lb.get('id'))
self.delete(self.LB_PATH.format(lb_id=lb.get('id')))
lb_json = {'vip': {'floating_ip_id': '1234'}}
lb_json = {'vip': {'network_id': '1234'}}
self.put(self.LB_PATH.format(lb_id=lb.get('id')), lb_json, status=409)
def test_delete_pending_delete(self):

View File

@ -92,11 +92,9 @@ class AllRepositoriesTest(base.OctaviaDBTestBase):
lb = {'name': 'test1', 'description': 'desc1', 'enabled': True,
'provisioning_status': constants.PENDING_UPDATE,
'operating_status': constants.OFFLINE}
vip = {'floating_ip_id': uuidutils.generate_uuid(),
'floating_ip_network_id': uuidutils.generate_uuid(),
'ip_address': '10.0.0.1',
'net_port_id': uuidutils.generate_uuid(),
'subnet_id': uuidutils.generate_uuid()}
vip = {'ip_address': '10.0.0.1',
'port_id': uuidutils.generate_uuid(),
'network_id': uuidutils.generate_uuid()}
lb_dm = self.repos.create_load_balancer_and_vip(self.session, lb, vip)
lb_dm_dict = lb_dm.to_dict()
del lb_dm_dict['vip']

View File

@ -83,10 +83,8 @@ class TestVip(base.BaseTypesTest):
def test_vip(self):
body = {"vip": {"ip_address": "10.0.0.1",
"net_port_id": uuidutils.generate_uuid(),
"subnet_id": uuidutils.generate_uuid(),
"floating_ip_id": uuidutils.generate_uuid(),
"floating_ip_network_id": uuidutils.generate_uuid()}}
"port_id": uuidutils.generate_uuid(),
"network_id": uuidutils.generate_uuid()}}
wsme_json.fromjson(self._type, body)
def test_invalid_ip_address(self):
@ -94,22 +92,12 @@ class TestVip(base.BaseTypesTest):
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_net_port_id(self):
body = {"net_port_id": "invalid_uuid"}
def test_invalid_port_id(self):
body = {"port_id": "invalid_uuid"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_subnet_id(self):
body = {"subnet_id": "invalid_uuid"}
def test_invalid_network_id(self):
body = {"network_id": "invalid_uuid"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_floating_ip_id(self):
body = {"floating_ip_id": "invalid_uuid"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)
def test_invalid_floating_network_ip_id(self):
body = {"floating_ip_network_id": "invalid_uuid"}
self.assertRaises(exc.InvalidInput, wsme_json.fromjson, self._type,
body)