Merge "Add port resource methods"
This commit is contained in:
commit
18cf7c2ccb
|
@ -1,6 +1,7 @@
|
||||||
pbr>=0.11,<2.0
|
pbr>=0.11,<2.0
|
||||||
|
|
||||||
bunch
|
bunch
|
||||||
|
decorator
|
||||||
jsonpatch
|
jsonpatch
|
||||||
os-client-config>=1.2.0
|
os-client-config>=1.2.0
|
||||||
six
|
six
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import operator
|
import operator
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from cinderclient.v1 import client as cinder_client
|
from cinderclient.v1 import client as cinder_client
|
||||||
|
from decorator import decorator
|
||||||
from dogpile import cache
|
from dogpile import cache
|
||||||
import glanceclient
|
import glanceclient
|
||||||
import glanceclient.exc
|
import glanceclient.exc
|
||||||
|
@ -60,6 +62,32 @@ OBJECT_CONTAINER_ACLS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def valid_kwargs(*valid_args):
|
||||||
|
# This decorator checks if argument passed as **kwargs to a function are
|
||||||
|
# present in valid_args.
|
||||||
|
#
|
||||||
|
# Typically, valid_kwargs is used when we want to distinguish between
|
||||||
|
# None and omitted arguments and we still want to validate the argument
|
||||||
|
# list.
|
||||||
|
#
|
||||||
|
# Example usage:
|
||||||
|
#
|
||||||
|
# @valid_kwargs('opt_arg1', 'opt_arg2')
|
||||||
|
# def my_func(self, mandatory_arg1, mandatory_arg2, **kwargs):
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
@decorator
|
||||||
|
def func_wrapper(func, *args, **kwargs):
|
||||||
|
argspec = inspect.getargspec(func)
|
||||||
|
for k in kwargs:
|
||||||
|
if k not in argspec.args[1:] and k not in valid_args:
|
||||||
|
raise TypeError(
|
||||||
|
"{f}() got an unexpected keyword argument "
|
||||||
|
"'{arg}'".format(f=inspect.stack()[1][3], arg=k))
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return func_wrapper
|
||||||
|
|
||||||
|
|
||||||
def openstack_clouds(config=None, debug=False):
|
def openstack_clouds(config=None, debug=False):
|
||||||
if not config:
|
if not config:
|
||||||
config = os_client_config.OpenStackConfig()
|
config = os_client_config.OpenStackConfig()
|
||||||
|
@ -732,6 +760,10 @@ class OpenStackCloud(object):
|
||||||
subnets = self.list_subnets()
|
subnets = self.list_subnets()
|
||||||
return _utils._filter_list(subnets, name_or_id, filters)
|
return _utils._filter_list(subnets, name_or_id, filters)
|
||||||
|
|
||||||
|
def search_ports(self, name_or_id=None, filters=None):
|
||||||
|
ports = self.list_ports()
|
||||||
|
return _utils._filter_list(ports, name_or_id, filters)
|
||||||
|
|
||||||
def search_volumes(self, name_or_id=None, filters=None):
|
def search_volumes(self, name_or_id=None, filters=None):
|
||||||
volumes = self.list_volumes()
|
volumes = self.list_volumes()
|
||||||
return _utils._filter_list(volumes, name_or_id, filters)
|
return _utils._filter_list(volumes, name_or_id, filters)
|
||||||
|
@ -780,6 +812,16 @@ class OpenStackCloud(object):
|
||||||
raise OpenStackCloudException(
|
raise OpenStackCloudException(
|
||||||
"Error fetching subnet list: %s" % e)
|
"Error fetching subnet list: %s" % e)
|
||||||
|
|
||||||
|
def list_ports(self):
|
||||||
|
try:
|
||||||
|
return self.manager.submitTask(_tasks.PortList())['ports']
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug(
|
||||||
|
"neutron could not list ports: {msg}".format(
|
||||||
|
msg=str(e)), exc_info=True)
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"error fetching port list: {msg}".format(msg=str(e)))
|
||||||
|
|
||||||
@_cache_on_arguments(should_cache_fn=_no_pending_volumes)
|
@_cache_on_arguments(should_cache_fn=_no_pending_volumes)
|
||||||
def list_volumes(self, cache=True):
|
def list_volumes(self, cache=True):
|
||||||
if not cache:
|
if not cache:
|
||||||
|
@ -948,6 +990,9 @@ class OpenStackCloud(object):
|
||||||
def get_subnet(self, name_or_id, filters=None):
|
def get_subnet(self, name_or_id, filters=None):
|
||||||
return _utils._get_entity(self.search_subnets, name_or_id, filters)
|
return _utils._get_entity(self.search_subnets, name_or_id, filters)
|
||||||
|
|
||||||
|
def get_port(self, name_or_id, filters=None):
|
||||||
|
return _utils._get_entity(self.search_ports, name_or_id, filters)
|
||||||
|
|
||||||
def get_volume(self, name_or_id, filters=None):
|
def get_volume(self, name_or_id, filters=None):
|
||||||
return _utils._get_entity(self.search_volumes, name_or_id, filters)
|
return _utils._get_entity(self.search_volumes, name_or_id, filters)
|
||||||
|
|
||||||
|
@ -2163,6 +2208,125 @@ class OpenStackCloud(object):
|
||||||
# a dict).
|
# a dict).
|
||||||
return new_subnet['subnet']
|
return new_subnet['subnet']
|
||||||
|
|
||||||
|
@valid_kwargs('name', 'admin_state_up', 'mac_address', 'fixed_ips',
|
||||||
|
'subnet_id', 'ip_address', 'security_groups',
|
||||||
|
'allowed_address_pairs', 'extra_dhcp_opts', 'device_owner',
|
||||||
|
'device_id')
|
||||||
|
def create_port(self, network_id, **kwargs):
|
||||||
|
"""Create a port
|
||||||
|
|
||||||
|
:param network_id: The ID of the network. (Required)
|
||||||
|
:param name: A symbolic name for the port. (Optional)
|
||||||
|
:param admin_state_up: The administrative status of the port,
|
||||||
|
which is up (true, default) or down (false). (Optional)
|
||||||
|
:param mac_address: The MAC address. (Optional)
|
||||||
|
:param fixed_ips: If you specify only a subnet ID, OpenStack Networking
|
||||||
|
allocates an available IP from that subnet to the port. (Optional)
|
||||||
|
:param subnet_id: If you specify only a subnet ID, OpenStack Networking
|
||||||
|
allocates an available IP from that subnet to the port. (Optional)
|
||||||
|
If you specify both a subnet ID and an IP address, OpenStack
|
||||||
|
Networking tries to allocate the specified address to the port.
|
||||||
|
:param ip_address: If you specify both a subnet ID and an IP address,
|
||||||
|
OpenStack Networking tries to allocate the specified address to
|
||||||
|
the port.
|
||||||
|
:param security_groups: List of security group UUIDs. (Optional)
|
||||||
|
:param allowed_address_pairs: Allowed address pairs list (Optional)
|
||||||
|
For example::
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ip_address": "23.23.23.1",
|
||||||
|
"mac_address": "fa:16:3e:c4:cd:3f"
|
||||||
|
}, ...
|
||||||
|
]
|
||||||
|
:param extra_dhcp_opts: Extra DHCP options. (Optional).
|
||||||
|
For example::
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"opt_name": "opt name1",
|
||||||
|
"opt_value": "value1"
|
||||||
|
}, ...
|
||||||
|
]
|
||||||
|
:param device_owner: The ID of the entity that uses this port.
|
||||||
|
For example, a DHCP agent. (Optional)
|
||||||
|
:param device_id: The ID of the device that uses this port.
|
||||||
|
For example, a virtual server. (Optional)
|
||||||
|
|
||||||
|
:returns: a dictionary describing the created port.
|
||||||
|
|
||||||
|
:raises: ``OpenStackCloudException`` on operation error.
|
||||||
|
"""
|
||||||
|
kwargs['network_id'] = network_id
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.manager.submitTask(
|
||||||
|
_tasks.PortCreate(body={'port': kwargs}))['port']
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug("failed to create a new port for network"
|
||||||
|
"'{net}'".format(net=network_id),
|
||||||
|
exc_info=True)
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"error creating a new port for network "
|
||||||
|
"'{net}': {msg}".format(net=network_id, msg=str(e)))
|
||||||
|
|
||||||
|
@valid_kwargs('name', 'admin_state_up', 'fixed_ips', 'security_groups')
|
||||||
|
def update_port(self, name_or_id, **kwargs):
|
||||||
|
"""Update a port
|
||||||
|
|
||||||
|
Note: to unset an attribute use None value. To leave an attribute
|
||||||
|
untouched just omit it.
|
||||||
|
|
||||||
|
:param name_or_id: name or id of the port to update. (Required)
|
||||||
|
:param name: A symbolic name for the port. (Optional)
|
||||||
|
:param admin_state_up: The administrative status of the port,
|
||||||
|
which is up (true) or down (false). (Optional)
|
||||||
|
:param fixed_ips: If you specify only a subnet ID, OpenStack Networking
|
||||||
|
allocates an available IP from that subnet to the port. (Optional)
|
||||||
|
:param security_groups: List of security group UUIDs. (Optional)
|
||||||
|
|
||||||
|
:returns: a dictionary describing the updated port.
|
||||||
|
|
||||||
|
:raises: OpenStackCloudException on operation error.
|
||||||
|
"""
|
||||||
|
port = self.get_port(name_or_id=name_or_id)
|
||||||
|
if port is None:
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"failed to find port '{port}'".format(port=name_or_id))
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.manager.submitTask(
|
||||||
|
_tasks.PortUpdate(
|
||||||
|
port=port['id'], body={'port': kwargs}))['port']
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug("failed to update port '{port}'".format(
|
||||||
|
port=name_or_id), exc_info=True)
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"failed to update port '{port}': {msg}".format(
|
||||||
|
port=name_or_id, msg=str(e)))
|
||||||
|
|
||||||
|
def delete_port(self, name_or_id):
|
||||||
|
"""Delete a port
|
||||||
|
|
||||||
|
:param name_or_id: id or name of the port to delete.
|
||||||
|
|
||||||
|
:returns: None.
|
||||||
|
|
||||||
|
:raises: OpenStackCloudException on operation error.
|
||||||
|
"""
|
||||||
|
port = self.get_port(name_or_id=name_or_id)
|
||||||
|
if port is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.manager.submitTask(_tasks.PortDelete(port=port['id']))
|
||||||
|
except Exception as e:
|
||||||
|
self.log.debug("failed to delete port '{port}'".format(
|
||||||
|
port=name_or_id), exc_info=True)
|
||||||
|
raise OpenStackCloudException(
|
||||||
|
"failed to delete port '{port}': {msg}".format(
|
||||||
|
port=name_or_id, msg=str(e)))
|
||||||
|
|
||||||
|
|
||||||
class OperatorCloud(OpenStackCloud):
|
class OperatorCloud(OpenStackCloud):
|
||||||
"""Represent a privileged/operator connection to an OpenStack Cloud.
|
"""Represent a privileged/operator connection to an OpenStack Cloud.
|
||||||
|
|
|
@ -283,6 +283,26 @@ class SubnetUpdate(task_manager.Task):
|
||||||
return client.neutron_client.update_subnet(**self.args)
|
return client.neutron_client.update_subnet(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class PortList(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.neutron_client.list_ports(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class PortCreate(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.neutron_client.create_port(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class PortUpdate(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.neutron_client.update_port(**self.args)
|
||||||
|
|
||||||
|
|
||||||
|
class PortDelete(task_manager.Task):
|
||||||
|
def main(self, client):
|
||||||
|
return client.neutron_client.delete_port(**self.args)
|
||||||
|
|
||||||
|
|
||||||
class MachineCreate(task_manager.Task):
|
class MachineCreate(task_manager.Task):
|
||||||
def main(self, client):
|
def main(self, client):
|
||||||
return client.ironic_client.node.create(**self.args)
|
return client.ironic_client.node.create(**self.args)
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_port
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Functional tests for `shade` port resource.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
|
||||||
|
from shade import openstack_cloud
|
||||||
|
from shade.exc import OpenStackCloudException
|
||||||
|
from shade.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestPort(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPort, self).setUp()
|
||||||
|
# Shell should have OS-* envvars from openrc, typically loaded by job
|
||||||
|
self.cloud = openstack_cloud()
|
||||||
|
# Skip Neutron tests if neutron is not present
|
||||||
|
if not self.cloud.has_service('network'):
|
||||||
|
self.skipTest('Network service not supported by cloud')
|
||||||
|
|
||||||
|
# Generate a unique port name to allow concurrent tests
|
||||||
|
self.new_port_name = 'test_' + ''.join(
|
||||||
|
random.choice(string.ascii_lowercase) for _ in range(5))
|
||||||
|
|
||||||
|
self.addCleanup(self._cleanup_ports)
|
||||||
|
|
||||||
|
def _cleanup_ports(self):
|
||||||
|
exception_list = list()
|
||||||
|
|
||||||
|
for p in self.cloud.list_ports():
|
||||||
|
if p['name'].startswith(self.new_port_name):
|
||||||
|
try:
|
||||||
|
self.cloud.delete_port(name_or_id=p['id'])
|
||||||
|
except Exception as e:
|
||||||
|
# We were unable to delete this port, let's try with next
|
||||||
|
exception_list.append(e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if exception_list:
|
||||||
|
# Raise an error: we must make users aware that something went
|
||||||
|
# wrong
|
||||||
|
raise OpenStackCloudException('\n'.join(exception_list))
|
||||||
|
|
||||||
|
def test_create_port(self):
|
||||||
|
port_name = self.new_port_name + '_create'
|
||||||
|
|
||||||
|
networks = self.cloud.list_networks()
|
||||||
|
if not networks:
|
||||||
|
self.assertFalse('no sensible network available')
|
||||||
|
|
||||||
|
port = self.cloud.create_port(
|
||||||
|
network_id=networks[0]['id'], name=port_name)
|
||||||
|
self.assertIsInstance(port, dict)
|
||||||
|
self.assertTrue('id' in port)
|
||||||
|
self.assertEqual(port.get('name'), port_name)
|
||||||
|
|
||||||
|
def test_get_port(self):
|
||||||
|
port_name = self.new_port_name + '_get'
|
||||||
|
|
||||||
|
networks = self.cloud.list_networks()
|
||||||
|
if not networks:
|
||||||
|
self.assertFalse('no sensible network available')
|
||||||
|
|
||||||
|
port = self.cloud.create_port(
|
||||||
|
network_id=networks[0]['id'], name=port_name)
|
||||||
|
self.assertIsInstance(port, dict)
|
||||||
|
self.assertTrue('id' in port)
|
||||||
|
self.assertEqual(port.get('name'), port_name)
|
||||||
|
|
||||||
|
updated_port = self.cloud.get_port(name_or_id=port['id'])
|
||||||
|
# extra_dhcp_opts is added later by Neutron...
|
||||||
|
if 'extra_dhcp_opts' in updated_port:
|
||||||
|
del updated_port['extra_dhcp_opts']
|
||||||
|
self.assertEqual(port, updated_port)
|
||||||
|
|
||||||
|
def test_update_port(self):
|
||||||
|
port_name = self.new_port_name + '_update'
|
||||||
|
new_port_name = port_name + '_new'
|
||||||
|
|
||||||
|
networks = self.cloud.list_networks()
|
||||||
|
if not networks:
|
||||||
|
self.assertFalse('no sensible network available')
|
||||||
|
|
||||||
|
self.cloud.create_port(
|
||||||
|
network_id=networks[0]['id'], name=port_name)
|
||||||
|
|
||||||
|
port = self.cloud.update_port(name_or_id=port_name,
|
||||||
|
name=new_port_name)
|
||||||
|
self.assertIsInstance(port, dict)
|
||||||
|
self.assertEqual(port.get('name'), new_port_name)
|
||||||
|
|
||||||
|
updated_port = self.cloud.get_port(name_or_id=port['id'])
|
||||||
|
self.assertEqual(port.get('name'), new_port_name)
|
||||||
|
self.assertEqual(port, updated_port)
|
||||||
|
|
||||||
|
def test_delete_port(self):
|
||||||
|
port_name = self.new_port_name + '_delete'
|
||||||
|
|
||||||
|
networks = self.cloud.list_networks()
|
||||||
|
if not networks:
|
||||||
|
self.assertFalse('no sensible network available')
|
||||||
|
|
||||||
|
port = self.cloud.create_port(
|
||||||
|
network_id=networks[0]['id'], name=port_name)
|
||||||
|
self.assertIsInstance(port, dict)
|
||||||
|
self.assertTrue('id' in port)
|
||||||
|
self.assertEqual(port.get('name'), port_name)
|
||||||
|
|
||||||
|
updated_port = self.cloud.get_port(name_or_id=port['id'])
|
||||||
|
self.assertIsNotNone(updated_port)
|
||||||
|
|
||||||
|
self.cloud.delete_port(name_or_id=port_name)
|
||||||
|
|
||||||
|
updated_port = self.cloud.get_port(name_or_id=port['id'])
|
||||||
|
self.assertIsNone(updated_port)
|
|
@ -0,0 +1,268 @@
|
||||||
|
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
test_port
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Test port resource (managed by neutron)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mock import patch
|
||||||
|
from shade import OpenStackCloud
|
||||||
|
from shade.exc import OpenStackCloudException
|
||||||
|
from shade.tests.unit import base
|
||||||
|
|
||||||
|
|
||||||
|
class TestPort(base.TestCase):
|
||||||
|
mock_neutron_port_create_rep = {
|
||||||
|
'port': {
|
||||||
|
'status': 'DOWN',
|
||||||
|
'binding:host_id': '',
|
||||||
|
'name': 'test-port-name',
|
||||||
|
'allowed_address_pairs': [],
|
||||||
|
'admin_state_up': True,
|
||||||
|
'network_id': 'test-net-id',
|
||||||
|
'tenant_id': 'test-tenant-id',
|
||||||
|
'binding:vif_details': {},
|
||||||
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:vif_type': 'unbound',
|
||||||
|
'device_owner': '',
|
||||||
|
'mac_address': '50:1c:0d:e4:f0:0d',
|
||||||
|
'binding:profile': {},
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'subnet_id': 'test-subnet-id',
|
||||||
|
'ip_address': '29.29.29.29'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'id': 'test-port-id',
|
||||||
|
'security_groups': [],
|
||||||
|
'device_id': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_neutron_port_update_rep = {
|
||||||
|
'port': {
|
||||||
|
'status': 'DOWN',
|
||||||
|
'binding:host_id': '',
|
||||||
|
'name': 'test-port-name-updated',
|
||||||
|
'allowed_address_pairs': [],
|
||||||
|
'admin_state_up': True,
|
||||||
|
'network_id': 'test-net-id',
|
||||||
|
'tenant_id': 'test-tenant-id',
|
||||||
|
'binding:vif_details': {},
|
||||||
|
'binding:vnic_type': 'normal',
|
||||||
|
'binding:vif_type': 'unbound',
|
||||||
|
'device_owner': '',
|
||||||
|
'mac_address': '50:1c:0d:e4:f0:0d',
|
||||||
|
'binding:profile': {},
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'subnet_id': 'test-subnet-id',
|
||||||
|
'ip_address': '29.29.29.29'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'id': 'test-port-id',
|
||||||
|
'security_groups': [],
|
||||||
|
'device_id': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_neutron_port_list_rep = {
|
||||||
|
'ports': [
|
||||||
|
{
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'binding:host_id': 'devstack',
|
||||||
|
'name': 'first-port',
|
||||||
|
'allowed_address_pairs': [],
|
||||||
|
'admin_state_up': True,
|
||||||
|
'network_id': '70c1db1f-b701-45bd-96e0-a313ee3430b3',
|
||||||
|
'tenant_id': '',
|
||||||
|
'extra_dhcp_opts': [],
|
||||||
|
'binding:vif_details': {
|
||||||
|
'port_filter': True,
|
||||||
|
'ovs_hybrid_plug': True
|
||||||
|
},
|
||||||
|
'binding:vif_type': 'ovs',
|
||||||
|
'device_owner': 'network:router_gateway',
|
||||||
|
'mac_address': 'fa:16:3e:58:42:ed',
|
||||||
|
'binding:profile': {},
|
||||||
|
'binding:vnic_type': 'normal',
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'subnet_id': '008ba151-0b8c-4a67-98b5-0d2b87666062',
|
||||||
|
'ip_address': '172.24.4.2'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'id': 'd80b1a3b-4fc1-49f3-952e-1e2ab7081d8b',
|
||||||
|
'security_groups': [],
|
||||||
|
'device_id': '9ae135f4-b6e0-4dad-9e91-3c223e385824'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'status': 'ACTIVE',
|
||||||
|
'binding:host_id': 'devstack',
|
||||||
|
'name': '',
|
||||||
|
'allowed_address_pairs': [],
|
||||||
|
'admin_state_up': True,
|
||||||
|
'network_id': 'f27aa545-cbdd-4907-b0c6-c9e8b039dcc2',
|
||||||
|
'tenant_id': 'd397de8a63f341818f198abb0966f6f3',
|
||||||
|
'extra_dhcp_opts': [],
|
||||||
|
'binding:vif_details': {
|
||||||
|
'port_filter': True,
|
||||||
|
'ovs_hybrid_plug': True
|
||||||
|
},
|
||||||
|
'binding:vif_type': 'ovs',
|
||||||
|
'device_owner': 'network:router_interface',
|
||||||
|
'mac_address': 'fa:16:3e:bb:3c:e4',
|
||||||
|
'binding:profile': {},
|
||||||
|
'binding:vnic_type': 'normal',
|
||||||
|
'fixed_ips': [
|
||||||
|
{
|
||||||
|
'subnet_id': '288bf4a1-51ba-43b6-9d0a-520e9005db17',
|
||||||
|
'ip_address': '10.0.0.1'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'id': 'f71a6703-d6de-4be1-a91a-a570ede1d159',
|
||||||
|
'security_groups': [],
|
||||||
|
'device_id': '9ae135f4-b6e0-4dad-9e91-3c223e385824'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPort, self).setUp()
|
||||||
|
self.client = OpenStackCloud('cloud', {})
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_create_port(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.create_port.return_value = \
|
||||||
|
self.mock_neutron_port_create_rep
|
||||||
|
|
||||||
|
port = self.client.create_port(
|
||||||
|
network_id='test-net-id', name='test-port-name',
|
||||||
|
admin_state_up=True)
|
||||||
|
|
||||||
|
mock_neutron_client.create_port.assert_called_with(
|
||||||
|
body={'port': dict(network_id='test-net-id', name='test-port-name',
|
||||||
|
admin_state_up=True)})
|
||||||
|
self.assertEqual(self.mock_neutron_port_create_rep['port'], port)
|
||||||
|
|
||||||
|
def test_create_port_parameters(self):
|
||||||
|
"""Test that we detect invalid arguments passed to create_port"""
|
||||||
|
self.assertRaises(
|
||||||
|
TypeError, self.client.create_port,
|
||||||
|
network_id='test-net-id', nome='test-port-name',
|
||||||
|
stato_amministrativo_porta=True)
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_create_port_exception(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.create_port.side_effect = Exception('blah')
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.create_port,
|
||||||
|
network_id='test-net-id', name='test-port-name',
|
||||||
|
admin_state_up=True)
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_update_port(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
mock_neutron_client.update_port.return_value = \
|
||||||
|
self.mock_neutron_port_update_rep
|
||||||
|
|
||||||
|
port = self.client.update_port(
|
||||||
|
name_or_id='d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b',
|
||||||
|
name='test-port-name-updated')
|
||||||
|
|
||||||
|
mock_neutron_client.update_port.assert_called_with(
|
||||||
|
port='d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b',
|
||||||
|
body={'port': dict(name='test-port-name-updated')})
|
||||||
|
self.assertEqual(self.mock_neutron_port_update_rep['port'], port)
|
||||||
|
|
||||||
|
def test_update_port_parameters(self):
|
||||||
|
"""Test that we detect invalid arguments passed to update_port"""
|
||||||
|
self.assertRaises(
|
||||||
|
TypeError, self.client.update_port,
|
||||||
|
name_or_id='test-port-id', nome='test-port-name-updated')
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_update_port_exception(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
mock_neutron_client.update_port.side_effect = Exception('blah')
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
OpenStackCloudException, self.client.update_port,
|
||||||
|
name_or_id='d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b',
|
||||||
|
name='test-port-name-updated')
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_list_ports(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
|
||||||
|
ports = self.client.list_ports()
|
||||||
|
|
||||||
|
mock_neutron_client.list_ports.assert_called_with()
|
||||||
|
self.assertItemsEqual(self.mock_neutron_port_list_rep['ports'], ports)
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_list_ports_exception(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.side_effect = Exception('blah')
|
||||||
|
|
||||||
|
self.assertRaises(OpenStackCloudException, self.client.list_ports)
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_search_ports_by_id(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
|
||||||
|
ports = self.client.search_ports(
|
||||||
|
name_or_id='f71a6703-d6de-4be1-a91a-a570ede1d159')
|
||||||
|
|
||||||
|
mock_neutron_client.list_ports.assert_called_with()
|
||||||
|
self.assertEquals(1, len(ports))
|
||||||
|
self.assertEquals('fa:16:3e:bb:3c:e4', ports[0]['mac_address'])
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_search_ports_by_name(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
|
||||||
|
ports = self.client.search_ports(name_or_id='first-port')
|
||||||
|
|
||||||
|
mock_neutron_client.list_ports.assert_called_with()
|
||||||
|
self.assertEquals(1, len(ports))
|
||||||
|
self.assertEquals('fa:16:3e:58:42:ed', ports[0]['mac_address'])
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_search_ports_not_found(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
|
||||||
|
ports = self.client.search_ports(name_or_id='non-existent')
|
||||||
|
|
||||||
|
mock_neutron_client.list_ports.assert_called_with()
|
||||||
|
self.assertEquals(0, len(ports))
|
||||||
|
|
||||||
|
@patch.object(OpenStackCloud, 'neutron_client')
|
||||||
|
def test_delete_port(self, mock_neutron_client):
|
||||||
|
mock_neutron_client.list_ports.return_value = \
|
||||||
|
self.mock_neutron_port_list_rep
|
||||||
|
|
||||||
|
self.client.delete_port(name_or_id='first-port')
|
||||||
|
|
||||||
|
mock_neutron_client.delete_port.assert_called_with(
|
||||||
|
port='d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b')
|
Loading…
Reference in New Issue