Merge "Add support for trunk ports and subports"
This commit is contained in:
@@ -134,6 +134,7 @@
|
|||||||
o-hm: true
|
o-hm: true
|
||||||
o-hk: true
|
o-hk: true
|
||||||
neutron-qos: true
|
neutron-qos: true
|
||||||
|
neutron-trunk: true
|
||||||
tox_environment:
|
tox_environment:
|
||||||
OPENSTACKSDK_HAS_OCTAVIA: 1
|
OPENSTACKSDK_HAS_OCTAVIA: 1
|
||||||
|
|
||||||
|
@@ -47,6 +47,7 @@ from openstack.network.v2 import service_profile as _service_profile
|
|||||||
from openstack.network.v2 import service_provider as _service_provider
|
from openstack.network.v2 import service_provider as _service_provider
|
||||||
from openstack.network.v2 import subnet as _subnet
|
from openstack.network.v2 import subnet as _subnet
|
||||||
from openstack.network.v2 import subnet_pool as _subnet_pool
|
from openstack.network.v2 import subnet_pool as _subnet_pool
|
||||||
|
from openstack.network.v2 import trunk as _trunk
|
||||||
from openstack.network.v2 import vpn_service as _vpn_service
|
from openstack.network.v2 import vpn_service as _vpn_service
|
||||||
from openstack import proxy
|
from openstack import proxy
|
||||||
from openstack import utils
|
from openstack import utils
|
||||||
@@ -3046,6 +3047,122 @@ class Proxy(proxy.Proxy):
|
|||||||
self._check_tag_support(resource)
|
self._check_tag_support(resource)
|
||||||
return resource.set_tags(self, tags)
|
return resource.set_tags(self, tags)
|
||||||
|
|
||||||
|
def create_trunk(self, **attrs):
|
||||||
|
"""Create a new trunk from attributes
|
||||||
|
|
||||||
|
:param dict attrs: Keyword arguments which will be used to create
|
||||||
|
a :class:`~openstack.network.v2.trunk.Trunk,
|
||||||
|
comprised of the properties on the Trunk class.
|
||||||
|
|
||||||
|
:returns: The results of trunk creation
|
||||||
|
:rtype: :class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
"""
|
||||||
|
return self._create(_trunk.Trunk, **attrs)
|
||||||
|
|
||||||
|
def delete_trunk(self, trunk, ignore_missing=True):
|
||||||
|
"""Delete a trunk
|
||||||
|
|
||||||
|
:param trunk: The value can be either the ID of trunk or a
|
||||||
|
:class:`openstack.network.v2.trunk.Trunk` instance
|
||||||
|
|
||||||
|
:returns: ``None``
|
||||||
|
"""
|
||||||
|
self._delete(_trunk.Trunk, trunk, ignore_missing=ignore_missing)
|
||||||
|
|
||||||
|
def find_trunk(self, name_or_id, ignore_missing=True, **args):
|
||||||
|
"""Find a single trunk
|
||||||
|
|
||||||
|
:param name_or_id: The name or ID of a trunk.
|
||||||
|
:param bool ignore_missing: When set to ``False``
|
||||||
|
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||||
|
raised when the resource does not exist.
|
||||||
|
When set to ``True``, None will be returned when
|
||||||
|
attempting to find a nonexistent resource.
|
||||||
|
:param dict args: Any additional parameters to be passed into
|
||||||
|
underlying methods. such as query filters.
|
||||||
|
:returns: One :class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
or None
|
||||||
|
"""
|
||||||
|
return self._find(_trunk.Trunk, name_or_id,
|
||||||
|
ignore_missing=ignore_missing, **args)
|
||||||
|
|
||||||
|
def get_trunk(self, trunk):
|
||||||
|
"""Get a single trunk
|
||||||
|
|
||||||
|
:param trunk: The value can be the ID of a trunk or a
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk` instance.
|
||||||
|
|
||||||
|
:returns: One
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||||
|
when no resource can be found.
|
||||||
|
"""
|
||||||
|
return self._get(_trunk.Trunk, trunk)
|
||||||
|
|
||||||
|
def trunks(self, **query):
|
||||||
|
"""Return a generator of trunks
|
||||||
|
|
||||||
|
:param dict query: Optional query parameters to be sent to limit
|
||||||
|
the resources being returned.
|
||||||
|
|
||||||
|
:returns: A generator of trunk objects
|
||||||
|
:rtype: :class:`~openstack.network.v2.trunk.trunk`
|
||||||
|
"""
|
||||||
|
return self._list(_trunk.Trunk, paginated=False, **query)
|
||||||
|
|
||||||
|
def update_trunk(self, trunk, **attrs):
|
||||||
|
"""Update a trunk
|
||||||
|
|
||||||
|
:param trunk: Either the id of a trunk or a
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk` instance.
|
||||||
|
:param dict attrs: The attributes to update on the trunk
|
||||||
|
represented by ``trunk``.
|
||||||
|
|
||||||
|
:returns: The updated trunk
|
||||||
|
:rtype: :class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
"""
|
||||||
|
return self._update(_trunk.Trunk, trunk, **attrs)
|
||||||
|
|
||||||
|
def add_trunk_subports(self, trunk, subports):
|
||||||
|
"""Set sub_ports on trunk
|
||||||
|
|
||||||
|
:param trunk: The value can be the ID of a trunk or a
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk` instance.
|
||||||
|
:param subports: New subports to be set.
|
||||||
|
:type subports: "list"
|
||||||
|
|
||||||
|
:returns: The updated trunk
|
||||||
|
:rtype: :class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
"""
|
||||||
|
trunk = self._get_resource(_trunk.Trunk, trunk)
|
||||||
|
return trunk.add_subports(self, subports)
|
||||||
|
|
||||||
|
def delete_trunk_subports(self, trunk, subports):
|
||||||
|
"""Remove sub_ports from trunk
|
||||||
|
|
||||||
|
:param trunk: The value can be the ID of a trunk or a
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk` instance.
|
||||||
|
:param subports: Subports to be removed.
|
||||||
|
:type subports: "list"
|
||||||
|
|
||||||
|
:returns: The updated trunk
|
||||||
|
:rtype: :class:`~openstack.network.v2.trunk.Trunk`
|
||||||
|
"""
|
||||||
|
trunk = self._get_resource(_trunk.Trunk, trunk)
|
||||||
|
return trunk.delete_subports(self, subports)
|
||||||
|
|
||||||
|
def get_trunk_subports(self, trunk):
|
||||||
|
"""Get sub_ports configured on trunk
|
||||||
|
|
||||||
|
:param trunk: The value can be the ID of a trunk or a
|
||||||
|
:class:`~openstack.network.v2.trunk.Trunk` instance.
|
||||||
|
|
||||||
|
:returns: Trunk sub_ports
|
||||||
|
:rtype: "list"
|
||||||
|
"""
|
||||||
|
trunk = self._get_resource(_trunk.Trunk, trunk)
|
||||||
|
return trunk.get_subports(self)
|
||||||
|
|
||||||
def create_vpn_service(self, **attrs):
|
def create_vpn_service(self, **attrs):
|
||||||
"""Create a new vpn service from attributes
|
"""Create a new vpn service from attributes
|
||||||
|
|
||||||
|
72
openstack/network/v2/trunk.py
Normal file
72
openstack/network/v2/trunk.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# 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 openstack.network import network_service
|
||||||
|
from openstack import resource
|
||||||
|
from openstack import utils
|
||||||
|
|
||||||
|
|
||||||
|
class Trunk(resource.Resource):
|
||||||
|
resource_key = 'trunk'
|
||||||
|
resources_key = 'trunks'
|
||||||
|
base_path = '/trunks'
|
||||||
|
service = network_service.NetworkService()
|
||||||
|
|
||||||
|
# capabilities
|
||||||
|
allow_create = True
|
||||||
|
allow_get = True
|
||||||
|
allow_update = True
|
||||||
|
allow_delete = True
|
||||||
|
allow_list = True
|
||||||
|
|
||||||
|
_query_mapping = resource.QueryParameters(
|
||||||
|
'name', 'description', 'port_id', 'status', 'sub_ports',
|
||||||
|
project_id='tenant_id',
|
||||||
|
is_admin_state_up='admin_state_up',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Properties
|
||||||
|
#: Trunk name.
|
||||||
|
name = resource.Body('name')
|
||||||
|
#: The ID of the project who owns the trunk. Only administrative
|
||||||
|
#: users can specify a project ID other than their own.
|
||||||
|
project_id = resource.Body('tenant_id')
|
||||||
|
#: The trunk description.
|
||||||
|
description = resource.Body('description')
|
||||||
|
#: The administrative state of the port, which is up ``True`` or
|
||||||
|
#: down ``False``. *Type: bool*
|
||||||
|
is_admin_state_up = resource.Body('admin_state_up', type=bool)
|
||||||
|
#: The ID of the trunk's parent port
|
||||||
|
port_id = resource.Body('port_id')
|
||||||
|
#: The status for the trunk. Possible values are ACTIVE, DOWN, BUILD,
|
||||||
|
#: DEGRADED, and ERROR.
|
||||||
|
status = resource.Body('status')
|
||||||
|
#: A list of ports associated with the trunk.
|
||||||
|
sub_ports = resource.Body('sub_ports', type=list)
|
||||||
|
|
||||||
|
def add_subports(self, session, subports):
|
||||||
|
url = utils.urljoin('/trunks', self.id, 'add_subports')
|
||||||
|
session.put(url, json={'sub_ports': subports})
|
||||||
|
self._body.attributes.update({'sub_ports': subports})
|
||||||
|
return self
|
||||||
|
|
||||||
|
def delete_subports(self, session, subports):
|
||||||
|
url = utils.urljoin('/trunks', self.id, 'remove_subports')
|
||||||
|
session.put(url, json={'sub_ports': subports})
|
||||||
|
self._body.attributes.update({'sub_ports': subports})
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_subports(self, session):
|
||||||
|
url = utils.urljoin('/trunks', self.id, 'get_subports')
|
||||||
|
resp = session.get(url)
|
||||||
|
self._body.attributes.update(resp.json())
|
||||||
|
return resp.json()
|
84
openstack/tests/functional/network/v2/test_trunk.py
Normal file
84
openstack/tests/functional/network/v2/test_trunk.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# 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 openstack.network.v2 import network
|
||||||
|
from openstack.network.v2 import port
|
||||||
|
from openstack.network.v2 import trunk as _trunk
|
||||||
|
from openstack.tests.functional import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestTrunk(base.BaseFunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTrunk, self).setUp()
|
||||||
|
self.TRUNK_NAME = self.getUniqueString()
|
||||||
|
self.TRUNK_NAME_UPDATED = self.getUniqueString()
|
||||||
|
net = self.conn.network.create_network()
|
||||||
|
assert isinstance(net, network.Network)
|
||||||
|
self.NET_ID = net.id
|
||||||
|
prt = self.conn.network.create_port(network_id=self.NET_ID)
|
||||||
|
assert isinstance(prt, port.Port)
|
||||||
|
self.PORT_ID = prt.id
|
||||||
|
self.ports_to_clean = [self.PORT_ID]
|
||||||
|
trunk = self.conn.network.create_trunk(
|
||||||
|
name=self.TRUNK_NAME,
|
||||||
|
port_id=self.PORT_ID)
|
||||||
|
assert isinstance(trunk, _trunk.Trunk)
|
||||||
|
self.TRUNK_ID = trunk.id
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.conn.network.delete_trunk(self.TRUNK_ID, ignore_missing=False)
|
||||||
|
for port_id in self.ports_to_clean:
|
||||||
|
self.conn.network.delete_port(port_id, ignore_missing=False)
|
||||||
|
self.conn.network.delete_network(self.NET_ID, ignore_missing=False)
|
||||||
|
super(TestTrunk, self).tearDown()
|
||||||
|
|
||||||
|
def test_find(self):
|
||||||
|
sot = self.conn.network.find_trunk(self.TRUNK_NAME)
|
||||||
|
self.assertEqual(self.TRUNK_ID, sot.id)
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
sot = self.conn.network.get_trunk(self.TRUNK_ID)
|
||||||
|
self.assertEqual(self.TRUNK_ID, sot.id)
|
||||||
|
self.assertEqual(self.TRUNK_NAME, sot.name)
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
ids = [o.id for o in self.conn.network.trunks()]
|
||||||
|
self.assertIn(self.TRUNK_ID, ids)
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
sot = self.conn.network.update_trunk(self.TRUNK_ID,
|
||||||
|
name=self.TRUNK_NAME_UPDATED)
|
||||||
|
self.assertEqual(self.TRUNK_NAME_UPDATED, sot.name)
|
||||||
|
|
||||||
|
def test_subports(self):
|
||||||
|
port_for_subport = self.conn.network.create_port(
|
||||||
|
network_id=self.NET_ID)
|
||||||
|
self.ports_to_clean.append(port_for_subport.id)
|
||||||
|
subports = [{
|
||||||
|
'port_id': port_for_subport.id,
|
||||||
|
'segmentation_type': 'vlan',
|
||||||
|
'segmentation_id': 111
|
||||||
|
}]
|
||||||
|
|
||||||
|
sot = self.conn.network.get_trunk_subports(self.TRUNK_ID)
|
||||||
|
self.assertEqual({'sub_ports': []}, sot)
|
||||||
|
|
||||||
|
self.conn.network.add_trunk_subports(self.TRUNK_ID, subports)
|
||||||
|
sot = self.conn.network.get_trunk_subports(self.TRUNK_ID)
|
||||||
|
self.assertEqual({'sub_ports': subports}, sot)
|
||||||
|
|
||||||
|
self.conn.network.delete_trunk_subports(
|
||||||
|
self.TRUNK_ID, [{'port_id': port_for_subport.id}])
|
||||||
|
sot = self.conn.network.get_trunk_subports(self.TRUNK_ID)
|
||||||
|
self.assertEqual({'sub_ports': []}, sot)
|
57
openstack/tests/unit/network/v2/test_trunk.py
Normal file
57
openstack/tests/unit/network/v2/test_trunk.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# 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 openstack.tests.unit import base
|
||||||
|
|
||||||
|
from openstack.network.v2 import trunk
|
||||||
|
|
||||||
|
EXAMPLE = {
|
||||||
|
'id': 'IDENTIFIER',
|
||||||
|
'description': 'Trunk description',
|
||||||
|
'name': 'trunk-name',
|
||||||
|
'tenant_id': '2',
|
||||||
|
'admin_state_up': True,
|
||||||
|
'port_id': 'fake_port_id',
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'sub_ports': [{
|
||||||
|
'port_id': 'subport_port_id',
|
||||||
|
'segmentation_id': 1234,
|
||||||
|
'segmentation_type': 'vlan'
|
||||||
|
}]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestQoSPolicy(base.TestCase):
|
||||||
|
|
||||||
|
def test_basic(self):
|
||||||
|
sot = trunk.Trunk()
|
||||||
|
self.assertEqual('trunk', sot.resource_key)
|
||||||
|
self.assertEqual('trunks', sot.resources_key)
|
||||||
|
self.assertEqual('/trunks', sot.base_path)
|
||||||
|
self.assertEqual('network', sot.service.service_type)
|
||||||
|
self.assertTrue(sot.allow_create)
|
||||||
|
self.assertTrue(sot.allow_get)
|
||||||
|
self.assertTrue(sot.allow_update)
|
||||||
|
self.assertTrue(sot.allow_delete)
|
||||||
|
self.assertTrue(sot.allow_list)
|
||||||
|
|
||||||
|
def test_make_it(self):
|
||||||
|
sot = trunk.Trunk(**EXAMPLE)
|
||||||
|
self.assertEqual(EXAMPLE['id'], sot.id)
|
||||||
|
self.assertEqual(EXAMPLE['description'], sot.description)
|
||||||
|
self.assertEqual(EXAMPLE['name'], sot.name)
|
||||||
|
self.assertEqual(EXAMPLE['tenant_id'], sot.project_id)
|
||||||
|
self.assertEqual(EXAMPLE['admin_state_up'], sot.is_admin_state_up)
|
||||||
|
self.assertEqual(EXAMPLE['port_id'], sot.port_id)
|
||||||
|
self.assertEqual(EXAMPLE['status'], sot.status)
|
||||||
|
self.assertEqual(EXAMPLE['sub_ports'], sot.sub_ports)
|
Reference in New Issue
Block a user