diff --git a/doc/source/main/octaviaapi.rst b/doc/source/main/octaviaapi.rst index 997276ce28..1b0e190289 100644 --- a/doc/source/main/octaviaapi.rst +++ b/doc/source/main/octaviaapi.rst @@ -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', diff --git a/octavia/api/v1/types/load_balancer.py b/octavia/api/v1/types/load_balancer.py index 2e4edc1522..073c494318 100644 --- a/octavia/api/v1/types/load_balancer.py +++ b/octavia/api/v1/types/load_balancer.py @@ -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): diff --git a/octavia/common/data_models.py b/octavia/common/data_models.py index c9bd5e3142..2c32f37107 100644 --- a/octavia/common/data_models.py +++ b/octavia/common/data_models.py @@ -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 diff --git a/octavia/db/migration/alembic_migrations/versions/14892634e228_update_vip.py b/octavia/db/migration/alembic_migrations/versions/14892634e228_update_vip.py new file mode 100644 index 0000000000..c67b80ba1d --- /dev/null +++ b/octavia/db/migration/alembic_migrations/versions/14892634e228_update_vip.py @@ -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)) diff --git a/octavia/db/models.py b/octavia/db/models.py index ca0d3596f4..e2e55df94f 100644 --- a/octavia/db/models.py +++ b/octavia/db/models.py @@ -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")) diff --git a/octavia/network/base.py b/octavia/network/base.py index e69de29bb2..cd8518cba1 100644 --- a/octavia/network/base.py +++ b/octavia/network/base.py @@ -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 + """ \ No newline at end of file diff --git a/octavia/network/data_models.py b/octavia/network/data_models.py new file mode 100644 index 0000000000..618cf68333 --- /dev/null +++ b/octavia/network/data_models.py @@ -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 diff --git a/octavia/tests/functional/api/v1/test_load_balancer.py b/octavia/tests/functional/api/v1/test_load_balancer.py index 504b50bf06..ac247ece1c 100644 --- a/octavia/tests/functional/api/v1/test_load_balancer.py +++ b/octavia/tests/functional/api/v1/test_load_balancer.py @@ -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): diff --git a/octavia/tests/functional/db/test_repositories.py b/octavia/tests/functional/db/test_repositories.py index b26ca75cc5..e575f915f5 100644 --- a/octavia/tests/functional/db/test_repositories.py +++ b/octavia/tests/functional/db/test_repositories.py @@ -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'] diff --git a/octavia/tests/unit/api/v1/types/test_load_balancers.py b/octavia/tests/unit/api/v1/types/test_load_balancers.py index 911878ccfe..9b34feb467 100644 --- a/octavia/tests/unit/api/v1/types/test_load_balancers.py +++ b/octavia/tests/unit/api/v1/types/test_load_balancers.py @@ -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) \ No newline at end of file