Add support for trunk ports and subports
This patch adds support for Neutron's trunks CRUD. It also adds subport for create/get/delete trunk's sub_ports. Change-Id: I835591ad517b84078a8ca87ef4d0bb93258c6e1c
This commit is contained in:
parent
abe16d3dd4
commit
ff4a261ae4
|
@ -134,6 +134,7 @@
|
|||
o-hm: true
|
||||
o-hk: true
|
||||
neutron-qos: true
|
||||
neutron-trunk: true
|
||||
tox_environment:
|
||||
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 subnet as _subnet
|
||||
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 import proxy
|
||||
from openstack import utils
|
||||
|
@ -3046,6 +3047,122 @@ class Proxy(proxy.Proxy):
|
|||
self._check_tag_support(resource)
|
||||
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):
|
||||
"""Create a new vpn service from attributes
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
Loading…
Reference in New Issue