Added Neutron API module
Implements bp join-tenant-network Change-Id: Ibda1c5e5ff903b289247cb212a239ea49ffa869a
This commit is contained in:
parent
f9636fa2c9
commit
cce07d59e1
@ -799,5 +799,16 @@
|
||||
# Driver to use for share creation (string value)
|
||||
#share_driver=manila.share.drivers.lvm.LVMShareDriver
|
||||
|
||||
#
|
||||
# Option defined in manila.network.neutron.api
|
||||
#
|
||||
|
||||
# Total option count: 173
|
||||
#neutron_url = http://127.0.0.1:9696
|
||||
#neutron_region_name = RegionOne
|
||||
#neutron_admin_tenant_name = service
|
||||
#neutron_auth_strategy = keystone
|
||||
#neutron_admin_auth_url = http://127.0.0.1:35357/v2.0
|
||||
#neutron_admin_password = %admin_pass
|
||||
#neutron_admin_username = neutron
|
||||
|
||||
# Total option count: 180
|
||||
|
@ -134,6 +134,10 @@ class ManilaException(Exception):
|
||||
super(ManilaException, self).__init__(message)
|
||||
|
||||
|
||||
class NetworkException(ManilaException):
|
||||
message = _("Exception due to network failure")
|
||||
|
||||
|
||||
class GlanceConnectionFailed(ManilaException):
|
||||
message = _("Connection to glance failed") + ": %(reason)s"
|
||||
|
||||
@ -435,6 +439,10 @@ class InvalidShare(ManilaException):
|
||||
message = _("Invalid share: %(reason)s")
|
||||
|
||||
|
||||
class PortLimitExceeded(QuotaError):
|
||||
message = _("Maximum number of ports exceeded")
|
||||
|
||||
|
||||
class ShareAccessNotFound(NotFound):
|
||||
message = _("Access_id %(access_id)s not found")
|
||||
|
||||
|
35
manila/network/__init__.py
Normal file
35
manila/network/__init__.py
Normal file
@ -0,0 +1,35 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Openstack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 manila.openstack.common.importutils
|
||||
from oslo.config import cfg
|
||||
|
||||
network_opts = [
|
||||
cfg.StrOpt('network_api_class',
|
||||
default='manila.network.neutron.api.API',
|
||||
help='The full class name of the '
|
||||
'network API class to use'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(network_opts)
|
||||
|
||||
|
||||
def API():
|
||||
importutils = manila.openstack.common.importutils
|
||||
network_api_class = cfg.CONF.network_api_class
|
||||
cls = importutils.import_class(network_api_class)
|
||||
return cls()
|
53
manila/network/neutron/__init__.py
Normal file
53
manila/network/neutron/__init__.py
Normal file
@ -0,0 +1,53 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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 neutronclient.common import exceptions
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from oslo.config import cfg
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def _get_client(token=None):
|
||||
params = {
|
||||
'endpoint_url': CONF.neutron_url,
|
||||
'timeout': CONF.neutron_url_timeout,
|
||||
'insecure': CONF.neutron_api_insecure,
|
||||
'ca_cert': CONF.neutron_ca_certificates_file,
|
||||
}
|
||||
|
||||
if token:
|
||||
params['token'] = token
|
||||
params['auth_strategy'] = None
|
||||
else:
|
||||
params['username'] = CONF.neutron_admin_username
|
||||
params['tenant_name'] = CONF.neutron_admin_tenant_name
|
||||
params['password'] = CONF.neutron_admin_password
|
||||
params['auth_url'] = CONF.neutron_admin_auth_url
|
||||
params['auth_strategy'] = CONF.neutron_auth_strategy
|
||||
return clientv20.Client(**params)
|
||||
|
||||
|
||||
def get_client(context):
|
||||
if context.is_admin:
|
||||
token = None
|
||||
elif not context.auth_token:
|
||||
raise exceptions.Unauthorized()
|
||||
else:
|
||||
token = context.auth_token
|
||||
|
||||
return _get_client(token=token)
|
168
manila/network/neutron/api.py
Normal file
168
manila/network/neutron/api.py
Normal file
@ -0,0 +1,168 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from neutronclient.common import exceptions as neutron_client_exc
|
||||
from oslo.config import cfg
|
||||
|
||||
from manila import context
|
||||
from manila.db import base
|
||||
from manila import exception
|
||||
from manila.network import neutron
|
||||
from manila.openstack.common.gettextutils import _
|
||||
from manila.openstack.common import log as logging
|
||||
|
||||
neutron_opts = [
|
||||
cfg.StrOpt('neutron_url',
|
||||
default='http://127.0.0.1:9696',
|
||||
deprecated_name='quantum_url',
|
||||
help='URL for connecting to neutron'),
|
||||
cfg.IntOpt('neutron_url_timeout',
|
||||
default=30,
|
||||
deprecated_name='quantum_url_timeout',
|
||||
help='timeout value for connecting to neutron in seconds'),
|
||||
cfg.StrOpt('neutron_admin_username',
|
||||
default='neutron',
|
||||
deprecated_name='quantum_admin_username',
|
||||
help='username for connecting to neutron in admin context'),
|
||||
cfg.StrOpt('neutron_admin_password',
|
||||
deprecated_name='quantum_admin_password',
|
||||
help='password for connecting to neutron in admin context',
|
||||
secret=True),
|
||||
cfg.StrOpt('neutron_admin_tenant_name',
|
||||
default='service',
|
||||
deprecated_name='quantum_admin_tenant_name',
|
||||
help='tenant name for connecting to neutron in admin context'),
|
||||
cfg.StrOpt('neutron_region_name',
|
||||
deprecated_name='quantum_region_name',
|
||||
help='region name for connecting to neutron in admin context'),
|
||||
cfg.StrOpt('neutron_admin_auth_url',
|
||||
deprecated_name='quantum_admin_auth_url',
|
||||
default='http://localhost:5000/v2.0',
|
||||
help='auth url for connecting to neutron in admin context'),
|
||||
cfg.BoolOpt('neutron_api_insecure',
|
||||
default=False,
|
||||
deprecated_name='quantum_api_insecure',
|
||||
help='if set, ignore any SSL validation issues'),
|
||||
cfg.StrOpt('neutron_auth_strategy',
|
||||
default='keystone',
|
||||
deprecated_name='quantum_auth_strategy',
|
||||
help='auth strategy for connecting to '
|
||||
'neutron in admin context'),
|
||||
# TODO(berrange) temporary hack until Neutron can pass over the
|
||||
# name of the OVS bridge it is configured with
|
||||
cfg.StrOpt('neutron_ovs_bridge',
|
||||
default='br-int',
|
||||
deprecated_name='quantum_ovs_bridge',
|
||||
help='Name of Integration Bridge used by Open vSwitch'),
|
||||
cfg.StrOpt('neutron_ca_certificates_file',
|
||||
help='Location of ca certificates file to use for '
|
||||
'neutron client requests.'),
|
||||
]
|
||||
|
||||
PORTBINDING_EXT = 'Port Binding'
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(neutron_opts)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class API(base.Base):
|
||||
"""API for interacting with the neutron 2.x API."""
|
||||
|
||||
def __init__(self):
|
||||
super(API, self).__init__()
|
||||
self.last_neutron_extension_sync = None
|
||||
self.extensions = {}
|
||||
self.client = neutron.get_client(context.get_admin_context())
|
||||
|
||||
def get_all_tenant_networks(self, tenant_id):
|
||||
search_opts = {'tenant_id': tenant_id, 'shared': False}
|
||||
nets = self.client.list_networks(**search_opts).get('networks', [])
|
||||
return nets
|
||||
|
||||
def create_port(self, tenant_id, network_id, host_id=None, subnet_id=None,
|
||||
fixed_ip=None, device_owner=None, device_id=None,
|
||||
mac_address=None, security_group_ids=None, dhcp_opts=None):
|
||||
try:
|
||||
port_req_body = {'port': {}}
|
||||
port_req_body['port']['network_id'] = network_id
|
||||
port_req_body['port']['admin_state_up'] = True
|
||||
port_req_body['port']['tenant_id'] = tenant_id
|
||||
if security_group_ids:
|
||||
port_req_body['port']['security_groups'] = security_group_ids
|
||||
if mac_address:
|
||||
port_req_body['port']['mac_address'] = mac_address
|
||||
if self._has_port_binding_extension() and host_id:
|
||||
port_req_body['port']['binding:host_id'] = host_id
|
||||
if dhcp_opts is not None:
|
||||
port_req_body['port']['extra_dhcp_opts'] = dhcp_opts
|
||||
if subnet_id:
|
||||
fixed_ip_dict = {'subnet_id': subnet_id}
|
||||
if fixed_ip:
|
||||
fixed_ip_dict.update({'ip_address': fixed_ip})
|
||||
port_req_body['port']['fixed_ips'] = [fixed_ip_dict]
|
||||
if device_owner:
|
||||
port_req_body['port']['device_owner'] = device_owner
|
||||
if device_id:
|
||||
port_req_body['port']['device_id'] = device_id
|
||||
port = self.client.create_port(port_req_body)
|
||||
return port
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
LOG.exception(_('Neutron error creating port on network %s') %
|
||||
network_id)
|
||||
if e.status_code == 409:
|
||||
raise exception.PortLimitExceeded()
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
|
||||
def delete_port(self, port_id):
|
||||
try:
|
||||
self.client.delete_port(port_id)
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
|
||||
def list_ports(self, **search_opts):
|
||||
"""List ports for the client based on search options."""
|
||||
return self.client.list_ports(**search_opts).get('ports')
|
||||
|
||||
def show_port(self, port_id):
|
||||
"""Return the port for the client given the port id."""
|
||||
try:
|
||||
return self.client.show_port(port_id).get('port')
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
|
||||
def get_all_networks(self):
|
||||
"""Get all networks for client."""
|
||||
return self.client.list_networks().get('networks')
|
||||
|
||||
def get_network(self, network_uuid):
|
||||
"""Get specific network for client."""
|
||||
try:
|
||||
network = self.client.show_network(network_uuid).get('network', {})
|
||||
return network
|
||||
except neutron_client_exc.NeutronClientException as e:
|
||||
raise exception.NetworkException(code=e.status_code,
|
||||
message=e.message)
|
||||
|
||||
def _has_port_binding_extension(self):
|
||||
if not self.extensions:
|
||||
extensions_list = self.client.list_extensions()['extensions']
|
||||
self.extensions = dict((ext['name'], ext)
|
||||
for ext in extensions_list)
|
||||
return PORTBINDING_EXT in self.extensions
|
118
manila/tests/fake_network.py
Normal file
118
manila/tests/fake_network.py
Normal file
@ -0,0 +1,118 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from manila.openstack.common import log as logging
|
||||
from manila.openstack.common import uuidutils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class API(object):
|
||||
"""Fake Network API"""
|
||||
network = {
|
||||
"status": "ACTIVE",
|
||||
"subnets": ["fake_subnet_id"],
|
||||
"name": "fake_network",
|
||||
"tenant_id": "fake_tenant_id",
|
||||
"shared": False,
|
||||
"id": "fake_id",
|
||||
"router:external": False,
|
||||
}
|
||||
|
||||
port = {
|
||||
"status": "ACTIVE",
|
||||
"allowed_address_pairs": [],
|
||||
"admin_state_up": True,
|
||||
"network_id": "fake_network_id",
|
||||
"tenant_id": "fake_tenant_id",
|
||||
"extra_dhcp_opts": [],
|
||||
"device_owner": "fake",
|
||||
"binding:capabilities": {"port_filter": True},
|
||||
"mac_address": "00:00:00:00:00:00",
|
||||
"fixed_ips": [
|
||||
{"subnet_id": "56537094-98d7-430a-b513-81c4dc6d9903",
|
||||
"ip_address": "10.12.12.10"}
|
||||
],
|
||||
"id": "fake_port_id",
|
||||
"security_groups": ["fake_sec_group_id"],
|
||||
"device_id": "fake_device_id"
|
||||
}
|
||||
|
||||
def get_all_tenant_networks(self, tenant_id):
|
||||
net1 = self.network.copy()
|
||||
net1['tenant_id'] = tenant_id
|
||||
net1['id'] = uuidutils.generate_uuid()
|
||||
|
||||
net2 = self.network.copy()
|
||||
net2['tenant_id'] = tenant_id
|
||||
net2['id'] = uuidutils.generate_uuid()
|
||||
return [net1, net2]
|
||||
|
||||
def create_port(self, tenant_id, network_id, subnet_id=None,
|
||||
fixed_ip=None, device_owner=None, device_id=None):
|
||||
port = self.port.copy()
|
||||
port['network_id'] = network_id
|
||||
port['admin_state_up'] = True
|
||||
port['tenant_id'] = tenant_id
|
||||
if fixed_ip:
|
||||
fixed_ip_dict = {'ip_address': fixed_ip}
|
||||
if subnet_id:
|
||||
fixed_ip_dict.update({'subnet_id': subnet_id})
|
||||
port['fixed_ips'] = [fixed_ip_dict]
|
||||
if device_owner:
|
||||
port['device_owner'] = device_owner
|
||||
if device_id:
|
||||
port['device_id'] = device_id
|
||||
return port
|
||||
|
||||
def list_ports(self, **search_opts):
|
||||
"""List ports for the client based on search options."""
|
||||
ports = []
|
||||
for i in range(2):
|
||||
ports.append(self.port.copy())
|
||||
for port in ports:
|
||||
port['id'] = uuidutils.generate_uuid()
|
||||
for key, val in search_opts.items():
|
||||
port[key] = val
|
||||
if 'id' in search_opts:
|
||||
return ports
|
||||
return ports
|
||||
|
||||
def show_port(self, port_id):
|
||||
"""Return the port for the client given the port id."""
|
||||
port = self.port.copy()
|
||||
port['id'] = port_id
|
||||
return port
|
||||
|
||||
def get_all_networks(self):
|
||||
"""Get all networks for client."""
|
||||
net1 = self.network.copy()
|
||||
net2 = self.network.copy()
|
||||
net1['id'] = uuidutils.generate_uuid()
|
||||
net2['id'] = uuidutils.generate_uuid()
|
||||
return [net1, net2]
|
||||
|
||||
def get_network(self, network_uuid):
|
||||
"""Get specific network for client."""
|
||||
network = self.network.copy()
|
||||
network['id'] = network_uuid
|
||||
return network
|
289
manila/tests/test_neutron.py
Normal file
289
manila/tests/test_neutron.py
Normal file
@ -0,0 +1,289 @@
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
from mock import Mock
|
||||
from mock import patch
|
||||
from neutronclient.common import exceptions as neutron_client_exc
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from oslo.config import cfg
|
||||
import unittest
|
||||
|
||||
from manila import context
|
||||
from manila.db import base
|
||||
from manila import exception
|
||||
from manila.network import neutron
|
||||
from manila.network.neutron import api as neutron_api
|
||||
from manila.tests.db import fakes
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class FakeNeutronClient(object):
|
||||
|
||||
def create_port(self, body):
|
||||
return body
|
||||
|
||||
def delete_port(self, port_id):
|
||||
pass
|
||||
|
||||
def show_port(self, port_id):
|
||||
pass
|
||||
|
||||
def list_ports(self, **search_opts):
|
||||
pass
|
||||
|
||||
def list_networks(self):
|
||||
pass
|
||||
|
||||
def show_network(self, network_uuid):
|
||||
pass
|
||||
|
||||
def list_extensions(self):
|
||||
pass
|
||||
|
||||
|
||||
class NeutronApiTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NeutronApiTest, self).setUp()
|
||||
self._create_neutron_api()
|
||||
|
||||
@patch.object(base, 'Base', fakes.FakeModel)
|
||||
@patch.object(context, 'get_admin_context',
|
||||
Mock(return_value='context'))
|
||||
@patch.object(neutron, 'get_client',
|
||||
Mock(return_value=FakeNeutronClient()))
|
||||
def _create_neutron_api(self):
|
||||
self.neutron_api = neutron_api.API()
|
||||
|
||||
@patch.object(base, 'Base', fakes.FakeModel)
|
||||
@patch.object(context, 'get_admin_context',
|
||||
Mock(return_value='context'))
|
||||
@patch.object(neutron, 'get_client', Mock())
|
||||
def test_create_api_object(self):
|
||||
with patch.object(base.Base, '__init__', Mock()):
|
||||
neutron_api_obj = neutron_api.API()
|
||||
base.Base.__init__.assert_called_once()
|
||||
neutron.get_client.assert_called_once_with('context')
|
||||
|
||||
def test_create_port_with_all_args(self):
|
||||
port_args = {'tenant_id': 'test tenant', 'network_id': 'test net',
|
||||
'host_id': 'test host', 'subnet_id': 'test subnet',
|
||||
'fixed_ip': 'test ip', 'device_owner': 'test owner',
|
||||
'device_id': 'test device', 'mac_address': 'test mac',
|
||||
'security_group_ids': 'test group',
|
||||
'dhcp_opts': 'test dhcp'}
|
||||
|
||||
with patch.object(self.neutron_api, '_has_port_binding_extension',
|
||||
Mock(return_value=True)):
|
||||
port = self.neutron_api.create_port(**port_args)
|
||||
self.assertEqual(port['port']['tenant_id'], port_args['tenant_id'])
|
||||
self.assertEqual(port['port']['network_id'],
|
||||
port_args['network_id'])
|
||||
self.assertEqual(port['port']['binding:host_id'],
|
||||
port_args['host_id'])
|
||||
self.assertEqual(port['port']['fixed_ips'][0]['subnet_id'],
|
||||
port_args['subnet_id'])
|
||||
self.assertEqual(port['port']['fixed_ips'][0]['ip_address'],
|
||||
port_args['fixed_ip'])
|
||||
self.assertEqual(port['port']['device_owner'],
|
||||
port_args['device_owner'])
|
||||
self.assertEqual(port['port']['device_id'], port_args['device_id'])
|
||||
self.assertEqual(port['port']['mac_address'],
|
||||
port_args['mac_address'])
|
||||
self.assertEqual(port['port']['security_groups'],
|
||||
port_args['security_group_ids'])
|
||||
self.assertEqual(port['port']['extra_dhcp_opts'],
|
||||
port_args['dhcp_opts'])
|
||||
|
||||
def test_create_port_with_required_args(self):
|
||||
port_args = {'tenant_id': 'test tenant', 'network_id': 'test net'}
|
||||
|
||||
with patch.object(self.neutron_api, '_has_port_binding_extension',
|
||||
Mock(return_value=True)):
|
||||
|
||||
port = self.neutron_api.create_port(**port_args)
|
||||
self.assertEqual(port['port']['tenant_id'], port_args['tenant_id'])
|
||||
self.assertEqual(port['port']['network_id'],
|
||||
port_args['network_id'])
|
||||
|
||||
@patch.object(neutron_api.LOG, 'exception', Mock())
|
||||
def test_create_port_exception(self):
|
||||
port_args = {'tenant_id': 'test tenant', 'network_id': 'test net'}
|
||||
client_create_port_mock = Mock(side_effect=
|
||||
neutron_client_exc.NeutronClientException)
|
||||
|
||||
with patch.object(self.neutron_api, '_has_port_binding_extension',
|
||||
Mock(return_value=True)):
|
||||
with patch.object(self.neutron_api.client, 'create_port',
|
||||
client_create_port_mock):
|
||||
|
||||
self.assertRaises(exception.NetworkException,
|
||||
self.neutron_api.create_port,
|
||||
**port_args)
|
||||
client_create_port_mock.assert_called_once()
|
||||
neutron_api.LOG.exception.assert_called_once()
|
||||
|
||||
@patch.object(neutron_api.LOG, 'exception', Mock())
|
||||
def test_create_port_exception_status_409(self):
|
||||
port_args = {'tenant_id': 'test tenant', 'network_id': 'test net'}
|
||||
client_create_port_mock = Mock(side_effect=
|
||||
neutron_client_exc.NeutronClientException(status_code=409))
|
||||
|
||||
with patch.object(self.neutron_api, '_has_port_binding_extension',
|
||||
Mock(return_value=True)):
|
||||
with patch.object(self.neutron_api.client, 'create_port',
|
||||
client_create_port_mock):
|
||||
|
||||
self.assertRaises(exception.PortLimitExceeded,
|
||||
self.neutron_api.create_port,
|
||||
**port_args)
|
||||
client_create_port_mock.assert_called_once()
|
||||
neutron_api.LOG.exception.assert_called_once()
|
||||
|
||||
def test_delete_port(self):
|
||||
port_id = 'test port id'
|
||||
with patch.object(self.neutron_api.client, 'delete_port',
|
||||
Mock()) as client_delete_port_mock:
|
||||
|
||||
self.neutron_api.delete_port(port_id)
|
||||
client_delete_port_mock.assert_called_once_with(port_id)
|
||||
|
||||
def test_list_ports(self):
|
||||
search_opts = {'test_option': 'test_value'}
|
||||
fake_ports = [{'fake port': 'fake port info'}]
|
||||
client_list_ports_mock = Mock(return_value={'ports': fake_ports})
|
||||
|
||||
with patch.object(self.neutron_api.client, 'list_ports',
|
||||
client_list_ports_mock):
|
||||
|
||||
ports = self.neutron_api.list_ports(**search_opts)
|
||||
client_list_ports_mock.assert_called_once_with(**search_opts)
|
||||
self.assertEqual(ports, fake_ports)
|
||||
|
||||
def test_show_port(self):
|
||||
port_id = 'test port id'
|
||||
fake_port = {'fake port': 'fake port info'}
|
||||
client_show_port_mock = Mock(return_value={'port': fake_port})
|
||||
|
||||
with patch.object(self.neutron_api.client, 'show_port',
|
||||
client_show_port_mock):
|
||||
|
||||
port = self.neutron_api.show_port(port_id)
|
||||
client_show_port_mock.assert_called_once_with(port_id)
|
||||
self.assertEqual(port, fake_port)
|
||||
|
||||
def test_get_network(self):
|
||||
network_id = 'test network id'
|
||||
fake_network = {'fake network': 'fake network info'}
|
||||
client_show_network_mock = Mock(return_value={'network': fake_network})
|
||||
|
||||
with patch.object(self.neutron_api.client, 'show_network',
|
||||
client_show_network_mock):
|
||||
|
||||
network = self.neutron_api.get_network(network_id)
|
||||
client_show_network_mock.assert_called_once_with(network_id)
|
||||
self.assertEqual(network, fake_network)
|
||||
|
||||
def test_get_all_network(self):
|
||||
fake_networks = [{'fake network': 'fake network info'}]
|
||||
client_list_networks_mock = Mock(
|
||||
return_value={'networks': fake_networks})
|
||||
|
||||
with patch.object(self.neutron_api.client, 'list_networks',
|
||||
client_list_networks_mock):
|
||||
|
||||
networks = self.neutron_api.get_all_networks()
|
||||
client_list_networks_mock.assert_called_once()
|
||||
self.assertEqual(networks, fake_networks)
|
||||
|
||||
def test_has_port_binding_extension_01(self):
|
||||
fake_extensions = [{'name': neutron_api.PORTBINDING_EXT}]
|
||||
client_list_ext_mock = Mock(
|
||||
return_value={'extensions': fake_extensions})
|
||||
|
||||
with patch.object(self.neutron_api.client, 'list_extensions',
|
||||
client_list_ext_mock):
|
||||
result = self.neutron_api._has_port_binding_extension()
|
||||
client_list_ext_mock.assert_called_once()
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_has_port_binding_extension_02(self):
|
||||
client_list_ext_mock = Mock()
|
||||
self.neutron_api.extensions =\
|
||||
{neutron_api.PORTBINDING_EXT: 'extension description'}
|
||||
|
||||
with patch.object(self.neutron_api.client, 'list_extensions',
|
||||
client_list_ext_mock):
|
||||
result = self.neutron_api._has_port_binding_extension()
|
||||
client_list_ext_mock.assert_not_called_once()
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_has_port_binding_extension_03(self):
|
||||
client_list_ext_mock = Mock()
|
||||
self.neutron_api.extensions =\
|
||||
{'neutron extension X': 'extension description'}
|
||||
|
||||
with patch.object(self.neutron_api.client, 'list_extensions',
|
||||
client_list_ext_mock):
|
||||
result = self.neutron_api._has_port_binding_extension()
|
||||
client_list_ext_mock.assert_not_called_once()
|
||||
self.assertFalse(result)
|
||||
|
||||
|
||||
class TestNeutronClient(unittest.TestCase):
|
||||
|
||||
@patch.object(clientv20.Client, '__init__', Mock(return_value=None))
|
||||
def test_get_client_with_token(self):
|
||||
client_args = {'endpoint_url': CONF.neutron_url,
|
||||
'timeout': CONF.neutron_url_timeout,
|
||||
'insecure': CONF.neutron_api_insecure,
|
||||
'ca_cert': CONF.neutron_ca_certificates_file,
|
||||
'token': 'test_token',
|
||||
'auth_strategy': None}
|
||||
my_context = context.RequestContext('test_user', 'test_tenant',
|
||||
auth_token='test_token',
|
||||
is_admin=False)
|
||||
|
||||
neutron.get_client(my_context)
|
||||
clientv20.Client.__init__.assert_called_once_with(**client_args)
|
||||
|
||||
@patch.object(clientv20.Client, '__init__', Mock(return_value=None))
|
||||
def test_get_client_no_token(self):
|
||||
my_context = context.RequestContext('test_user', 'test_tenant',
|
||||
is_admin=False)
|
||||
|
||||
self.assertRaises(neutron_client_exc.Unauthorized,
|
||||
neutron.get_client,
|
||||
my_context)
|
||||
|
||||
@patch.object(clientv20.Client, '__init__', Mock(return_value=None))
|
||||
def test_get_client_admin_context(self):
|
||||
client_args = {'endpoint_url': CONF.neutron_url,
|
||||
'timeout': CONF.neutron_url_timeout,
|
||||
'insecure': CONF.neutron_api_insecure,
|
||||
'ca_cert': CONF.neutron_ca_certificates_file,
|
||||
'username': CONF.neutron_admin_username,
|
||||
'tenant_name': CONF.neutron_admin_tenant_name,
|
||||
'password': CONF.neutron_admin_password,
|
||||
'auth_url': CONF.neutron_admin_auth_url,
|
||||
'auth_strategy': CONF.neutron_auth_strategy}
|
||||
my_context = context.RequestContext('test_user', 'test_tenant',
|
||||
is_admin=True)
|
||||
|
||||
neutron.get_client(my_context)
|
||||
clientv20.Client.__init__.assert_called_once_with(**client_args)
|
@ -13,6 +13,7 @@ oslo.config>=1.1.0
|
||||
paramiko>=1.8.0
|
||||
paste
|
||||
pastedeploy>=1.5.0
|
||||
python-neutronclient>=2.3.0,<3
|
||||
python-glanceclient>=0.5.0,<2
|
||||
python-keystoneclient>=0.3.0
|
||||
python-swiftclient>=1.2,<2
|
||||
|
Loading…
Reference in New Issue
Block a user