Add neutron support

- Add network driver to switch between nova-network (default) and neutron
- Add option to perform Security group management via neutron
- IP filter for instance detailed view (hiding trove owned/managed network info)
- Add unittests and intergration tests

Implements blueprint neutron-support

  Related patch:
    SHA:        bd54d99aa0

Change-Id: Ia0103bcda6590a4e387038ec035aee15454cdf48
This commit is contained in:
Anna Shen 2014-04-17 02:12:32 -07:00 committed by Nikhil Manchanda
parent 7e170cc14e
commit 90f225bf69
18 changed files with 595 additions and 71 deletions

View File

@ -59,6 +59,7 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
#nova_compute_url = http://localhost:8774/v2
#cinder_url = http://localhost:8776/v1
#swift_url = http://localhost:8080/v1/AUTH_
#neutron_url = http://localhost:9696/
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
@ -74,6 +75,8 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
#swift_service_type = object-store
# Service type to use when searching catalog.
#heat_service_type = orchestration
# Service type to use when searching catalog.
#neutron_service_type = network
# Config options for enabling volume service
trove_volume_support = True
@ -113,6 +116,10 @@ dns_instance_entry_factory = trove.dns.designate.driver.DesignateInstanceEntryFa
dns_endpoint_url = http://127.0.0.1/v1/
dns_service_type = dns
# Neutron
network_driver = trove.network.nova.NovaNetwork
default_neutron_networks =
# Trove Security Groups for Instances
trove_security_groups_support = True
trove_security_group_rule_cidr = 0.0.0.0/0
@ -126,8 +133,13 @@ agent_call_high_timeout = 150
use_nova_server_volume = False
# Config option for filtering the IP address that DNS uses
# For nova-network, set this to the appropriate network label defined in nova
# For neutron, set this to .* since users can specify custom network labels
# You can also optionally specify regex'es to match the actual IP addresses
# ip_regex (white-list) is applied before black_list_regex in the filter chain
network_label_regex = ^private$
#ip_regex = ^(15.|123.)
#black_list_regex = ^(10.0.0.)
# Datastore templates
template_path = /etc/trove/templates/

View File

@ -76,6 +76,7 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
#nova_compute_url = http://localhost:8774/v2
#cinder_url = http://localhost:8776/v1
#swift_url = http://localhost:8080/v1/AUTH_
#neutron_url = http://localhost:9696/
# nova_compute_url, cinder_url, swift_url, and heat_url can all be fetched
# from Keystone. To fetch from Keystone, comment out nova_compute_url,
@ -91,10 +92,18 @@ trove_auth_url = http://0.0.0.0:5000/v2.0
#swift_service_type = object-store
# Service type to use when searching catalog.
#heat_service_type = orchestration
# Service type to use when searching catalog.
#neutron_service_type = network
# Config option for showing the IP address that nova doles out
# For nova-network, set this to the appropriate network label defined in nova
# For neutron, set this to .* since users can specify custom network labels
# You can also optionally specify regex'es to match the actual IP addresses
# ip_regex (white-list) is applied before black_list_regex in the filter chain
network_label_regex = ^private$
#network_label_regex = .* //with neutron enabled
#ip_regex = ^(15.|123.)
#black_list_regex = ^10.0.0.
# Config options for enabling volume service
trove_volume_support = True
@ -129,6 +138,11 @@ dns_instance_entry_factory = trove.dns.designate.driver.DesignateInstanceEntryFa
dns_endpoint_url = http://127.0.0.1/v1/
dns_service_type = dns
# Neutron
network_driver = trove.network.nova.NovaNetwork
default_neutron_networks =
# Taskmanager queue name
taskmanager_queue = taskmanager

View File

@ -79,6 +79,7 @@ nova_service_name = Compute Service
# Config option for showing the IP address that nova doles out
network_label_regex = ^private$
ip_regex = ^(15.|123.)
black_list_regex = ^(10.0.0.)
# Config options for enabling volume service
trove_volume_support = True

View File

@ -17,6 +17,7 @@ python-cinderclient>=1.0.7
python-keystoneclient>=0.9.0
python-swiftclient>=2.0.2
python-designateclient>=1.0.0
python-neutronclient>=2.3.5,<3
iso8601>=0.1.9
jsonschema>=2.0.0,<3.0.0
Jinja2

View File

@ -55,6 +55,9 @@ common_opts = [
cfg.StrOpt('nova_compute_url', help='URL without the tenant segment.'),
cfg.StrOpt('nova_compute_service_type', default='compute',
help='Service type to use when searching catalog.'),
cfg.StrOpt('neutron_url', help='URL without the tenant segment.'),
cfg.StrOpt('neutron_service_type', default='network',
help='Service type to use when searching catalog.'),
cfg.StrOpt('cinder_url', help='URL without the tenant segment.'),
cfg.StrOpt('cinder_service_type', default='volumev2',
help='Service type to use when searching catalog.'),
@ -209,6 +212,8 @@ common_opts = [
default='trove.common.remote.guest_client'),
cfg.StrOpt('remote_nova_client',
default='trove.common.remote.nova_client'),
cfg.StrOpt('remote_neutron_client',
default='trove.common.remote.neutron_client'),
cfg.StrOpt('remote_cinder_client',
default='trove.common.remote.cinder_client'),
cfg.StrOpt('remote_heat_client',
@ -235,6 +240,7 @@ common_opts = [
help="Admin tenant used to connect to nova.", secret=True),
cfg.StrOpt('network_label_regex', default='^private$'),
cfg.StrOpt('ip_regex', default=None),
cfg.StrOpt('black_list_regex', default=None),
cfg.StrOpt('cloudinit_location', default='/etc/trove/cloudinit',
help="Path to folder with cloudinit scripts."),
cfg.StrOpt('guest_config',
@ -254,18 +260,21 @@ common_opts = [
default=['json'],
help='Filetype endings not to be reattached to an ID '
'by the utils method correct_id_with_req.'),
cfg.ListOpt('default_neutron_networks',
default=[],
help='List of network IDs which should be attached'
' to instance when networks are not specified'
' in API call.'),
cfg.ListOpt('default_neutron_networks', default=[],
help='List of IDs for management networks which should be '
' attached to the instance regardless of what NICs'
' are specified in the create API call.'),
cfg.IntOpt('max_header_line', default=16384,
help='Maximum line size of message headers to be accepted. '
'max_header_line may need to be increased when using '
'large tokens (typically those generated by the '
'Keystone v3 API with big service catalogs).'),
cfg.StrOpt('conductor_manager', default='trove.conductor.manager.Manager',
help='Qualified class name to use for conductor manager.')
help='Qualified class name to use for conductor manager.'),
cfg.StrOpt('network_driver', default='trove.network.nova.NovaNetwork',
help="Describes the actual network manager used for "
"the management of network attributes "
"(security groups, floating IPs, etc.)")
]
# Datastore specific option groups

View File

@ -15,7 +15,11 @@
"""Model classes that form the core of instances functionality."""
from trove.openstack.common.importutils import import_class
from trove.common import remote
from trove.common import cfg
CONF = cfg.CONF
class ModelBase(object):
@ -94,6 +98,17 @@ class RemoteModelBase(ModelBase):
return self._data_item(self._data_object)
class NetworkRemoteModelBase(RemoteModelBase):
network_driver = None
@classmethod
def get_driver(cls, context):
if not cls.network_driver:
cls.network_driver = import_class(CONF.network_driver)
return cls.network_driver(context)
class NovaRemoteModelBase(RemoteModelBase):
@classmethod

View File

@ -162,9 +162,25 @@ def swift_client(context):
return client
def neutron_client(context):
from neutronclient.v2_0 import client as NeutronClient
if CONF.neutron_url:
# neutron endpoint url / publicURL does not include tenant segment
url = CONF.neutron_url
else:
url = get_endpoint(context.service_catalog,
service_type=CONF.neutron_service_type,
endpoint_region=CONF.os_region_name)
client = NeutronClient.Client(token=context.auth_token,
endpoint_url=url)
return client
create_dns_client = import_class(CONF.remote_dns_client)
create_guest_client = import_class(CONF.remote_guest_client)
create_nova_client = import_class(CONF.remote_nova_client)
create_swift_client = import_class(CONF.remote_swift_client)
create_cinder_client = import_class(CONF.remote_cinder_client)
create_heat_client = import_class(CONF.remote_heat_client)
create_neutron_client = import_class(CONF.remote_neutron_client)

View File

@ -17,15 +17,13 @@
"""
Model classes for Security Groups and Security Group Rules on instances.
"""
import trove.common.remote
from trove.common import cfg
from trove.common import exception
from trove.db.models import DatabaseModelBase
from trove.common.models import NovaRemoteModelBase
from trove.common.models import NetworkRemoteModelBase
from trove.openstack.common import log as logging
from trove.openstack.common.gettextutils import _
from novaclient import exceptions as nova_exceptions
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -207,7 +205,7 @@ class SecurityGroupInstanceAssociation(DatabaseModelBase):
return association.instance_id
class RemoteSecurityGroup(NovaRemoteModelBase):
class RemoteSecurityGroup(NetworkRemoteModelBase):
_data_fields = ['id', 'name', 'description', 'rules']
@ -216,65 +214,37 @@ class RemoteSecurityGroup(NovaRemoteModelBase):
msg = "Security Group does not have id defined!"
raise exception.InvalidModelError(msg)
elif security_group is None:
try:
client = trove.common.remote.create_nova_client(context)
self._data_object = client.security_groups.get(id)
except nova_exceptions.NotFound as e:
raise exception.NotFound(id=id)
except nova_exceptions.ClientException as e:
raise exception.TroveError(str(e))
driver = self.get_driver(context)
self._data_object = driver.get_sec_group_by_id(group_id=id)
else:
self._data_object = security_group
@classmethod
def create(cls, name, description, context):
"""Creates a new Security Group."""
client = trove.common.remote.create_nova_client(context)
try:
sec_group = client.security_groups.create(name=name,
description=description)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to create remote security group')
raise exception.SecurityGroupCreationError(str(e))
driver = cls.get_driver(context)
sec_group = driver.create_security_group(
name=name, description=description)
return RemoteSecurityGroup(security_group=sec_group)
@classmethod
def delete(cls, sec_group_id, context):
client = trove.common.remote.create_nova_client(context)
try:
client.security_groups.delete(sec_group_id)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to delete remote security group')
raise exception.SecurityGroupDeletionError(str(e))
"""Deletes a Security Group."""
driver = cls.get_driver(context)
driver.delete_security_group(sec_group_id)
@classmethod
def add_rule(cls, sec_group_id, protocol, from_port,
to_port, cidr, context):
"""Adds a new rule to an existing security group."""
driver = cls.get_driver(context)
sec_group_rule = driver.add_security_group_rule(
sec_group_id, protocol, from_port, to_port, cidr)
client = trove.common.remote.create_nova_client(context)
try:
sec_group_rule = client.security_group_rules.create(
parent_group_id=sec_group_id,
ip_protocol=protocol,
from_port=from_port,
to_port=to_port,
cidr=cidr)
return sec_group_rule.id
except nova_exceptions.ClientException as e:
LOG.exception('Failed to add rule to remote security group')
raise exception.SecurityGroupRuleCreationError(str(e))
return sec_group_rule.id
@classmethod
def delete_rule(cls, sec_group_rule_id, context):
client = trove.common.remote.create_nova_client(context)
try:
client.security_group_rules.delete(sec_group_rule_id)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to delete rule to remote security group')
raise exception.SecurityGroupRuleDeletionError(str(e))
"""Deletes a rule from an existing security group."""
driver = cls.get_driver(context)
driver.delete_security_group_rule(sec_group_rule_id)

View File

@ -48,9 +48,12 @@ CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def filter_ips(ips, regex):
"""Filter out IPs not matching regex."""
return [ip for ip in ips if re.search(regex, ip)]
def filter_ips(ips, white_list_regex, black_list_regex):
"""Return IPs matching white_list_regex and
Filter out IPs matching black_list_regex.
"""
return [ip for ip in ips if re.search(white_list_regex, ip)
and not re.search(black_list_regex, ip)]
def load_server(context, instance_id, server_id):
@ -204,8 +207,8 @@ class SimpleInstance(object):
IPs.extend([addr.get('addr')
for addr in self.addresses[label]])
# Includes ip addresses that match the regexp pattern
if CONF.ip_regex:
IPs = filter_ips(IPs, CONF.ip_regex)
if CONF.ip_regex and CONF.black_list_regex:
IPs = filter_ips(IPs, CONF.ip_regex, CONF.black_list_regex)
return IPs
@property
@ -640,10 +643,11 @@ class Instance(BuiltInstance):
datastore1=backup_info.datastore.name,
datastore2=datastore.name)
if not nics and CONF.default_neutron_networks:
if not nics:
nics = []
for net_id in CONF.default_neutron_networks:
nics.append({"net-id": net_id})
if CONF.default_neutron_networks:
nics = [{"net-id": net_id}
for net_id in CONF.default_neutron_networks] + nics
def _create_resources():

View File

51
trove/network/base.py Normal file
View File

@ -0,0 +1,51 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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 abc
import six
@six.add_metaclass(abc.ABCMeta)
class NetworkDriver(object):
"""Base Network Driver class to abstract the network driver used."""
@abc.abstractmethod
def get_sec_group_by_id(self, group_id):
"""
Returns security group with given group_id
"""
@abc.abstractmethod
def create_security_group(self, name, description):
"""
Creates the security group with given name and description
"""
@abc.abstractmethod
def delete_security_group(self, sec_group_id):
"""Deletes the security group by given ID."""
@abc.abstractmethod
def add_security_group_rule(self, sec_group_id, protocol,
from_port, to_port, cidr):
"""
Adds the rule identified by the security group ID,
transport protocol, port range: from -> to, CIDR.
"""
@abc.abstractmethod
def delete_security_group_rule(self, sec_group_rule_id):
"""Deletes the rule by given ID."""

143
trove/network/neutron.py Normal file
View File

@ -0,0 +1,143 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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 trove.common import exception
from trove.common import remote
from trove.network import base
from trove.openstack.common import log as logging
from neutronclient.common import exceptions as neutron_exceptions
LOG = logging.getLogger(__name__)
CONST = {'IPv4': "IPv4",
'IPv6': "IPv6",
'INGRESS': "ingress",
'EGRESS': "egress",
'PROTO_NAME_TCP': 'tcp',
'PROTO_NAME_ICMP': 'icmp',
'PROTO_NAME_ICMP_V6': 'icmpv6',
'PROTO_NAME_UDP': 'udp'}
class NovaNetworkStruct(object):
def __init__(self, **properties):
self.__dict__.update(properties)
class NeutronDriver(base.NetworkDriver):
def __init__(self, context):
try:
self.client = remote.create_neutron_client(context)
except neutron_exceptions.NeutronClientException as e:
raise exception.TroveError(str(e))
def get_sec_group_by_id(self, group_id):
try:
return self.client.show_security_group(security_group=group_id)
except neutron_exceptions.NeutronClientException as e:
LOG.exception('Failed to get remote security group')
raise exception.TroveError(str(e))
def create_security_group(self, name, description):
try:
sec_group_body = {"security_group": {"name": name,
"description": description}}
sec_group = self.client.create_security_group(body=sec_group_body)
return self._convert_to_nova_security_group_format(
sec_group.get('security_group', sec_group))
except neutron_exceptions.NeutronClientException as e:
LOG.exception('Failed to create remote security group')
raise exception.SecurityGroupCreationError(str(e))
def delete_security_group(self, sec_group_id):
try:
self.client.delete_security_group(security_group=sec_group_id)
except neutron_exceptions.NeutronClientException as e:
LOG.exception('Failed to delete remote security group')
raise exception.SecurityGroupDeletionError(str(e))
def add_security_group_rule(self, sec_group_id, protocol,
from_port, to_port, cidr,
direction=CONST['INGRESS'],
ethertype=CONST['IPv4']):
try:
secgroup_rule_body = {"security_group_rule":
{"security_group_id": sec_group_id,
"protocol": protocol,
"port_range_min": from_port,
"port_range_max": to_port,
"remote_ip_prefix": cidr,
"direction": direction, # ingress | egress
"ethertype": ethertype, # IPv4 | IPv6
}}
secgroup_rule = self.client.create_security_group_rule(
secgroup_rule_body)
return self._convert_to_nova_security_group_rule_format(
secgroup_rule.get('security_group_rule', secgroup_rule))
except neutron_exceptions.NeutronClientException as e:
# ignore error if rule already exists
if e.status_code == 409:
LOG.exception("secgroup rule already exists")
else:
LOG.exception('Failed to add rule to remote security group')
raise exception.SecurityGroupRuleCreationError(str(e))
def delete_security_group_rule(self, sec_group_rule_id):
try:
self.client.delete_security_group_rule(
security_group_rule=sec_group_rule_id)
except neutron_exceptions.NeutronClientException as e:
LOG.exception('Failed to delete rule to remote security group')
raise exception.SecurityGroupRuleDeletionError(str(e))
def _convert_to_nova_security_group_format(self, security_group):
nova_group = {}
nova_group['id'] = security_group['id']
nova_group['description'] = security_group['description']
nova_group['name'] = security_group['name']
nova_group['project_id'] = security_group['tenant_id']
nova_group['rules'] = []
for rule in security_group.get('security_group_rules', []):
if rule['direction'] == 'ingress':
nova_group['rules'].append(
self._convert_to_nova_security_group_rule_format(rule))
return NovaNetworkStruct(**nova_group)
def _convert_to_nova_security_group_rule_format(self, rule):
nova_rule = {}
nova_rule['id'] = rule['id']
nova_rule['parent_group_id'] = rule['security_group_id']
nova_rule['protocol'] = rule['protocol']
if (nova_rule['protocol'] and rule.get('port_range_min') is None and
rule.get('port_range_max') is None):
if rule['protocol'].upper() in ['TCP', 'UDP']:
nova_rule['from_port'] = 1
nova_rule['to_port'] = 65535
else:
nova_rule['from_port'] = -1
nova_rule['to_port'] = -1
else:
nova_rule['from_port'] = rule.get('port_range_min')
nova_rule['to_port'] = rule.get('port_range_max')
nova_rule['group_id'] = rule['remote_group_id']
nova_rule['cidr'] = rule.get('remote_ip_prefix')
return NovaNetworkStruct(**nova_rule)

80
trove/network/nova.py Normal file
View File

@ -0,0 +1,80 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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 trove.common import exception
from trove.common import remote
from trove.network import base
from trove.openstack.common import log as logging
from novaclient import exceptions as nova_exceptions
LOG = logging.getLogger(__name__)
class NovaNetwork(base.NetworkDriver):
def __init__(self, context):
try:
self.client = remote.create_nova_client(
context)
except nova_exceptions.ClientException as e:
raise exception.TroveError(str(e))
def get_sec_group_by_id(self, group_id):
try:
return self.client.security_groups.get(group_id)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to get remote security group')
raise exception.TroveError(str(e))
def create_security_group(self, name, description):
try:
sec_group = self.client.security_groups.create(
name=name, description=description)
return sec_group
except nova_exceptions.ClientException as e:
LOG.exception('Failed to create remote security group')
raise exception.SecurityGroupCreationError(str(e))
def delete_security_group(self, sec_group_id):
try:
self.client.security_groups.delete(sec_group_id)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to delete remote security group')
raise exception.SecurityGroupDeletionError(str(e))
def add_security_group_rule(self, sec_group_id, protocol,
from_port, to_port, cidr):
try:
sec_group_rule = self.client.security_group_rules.create(
parent_group_id=sec_group_id,
ip_protocol=protocol,
from_port=from_port,
to_port=to_port,
cidr=cidr)
return sec_group_rule
except nova_exceptions.ClientException as e:
LOG.exception('Failed to add rule to remote security group')
raise exception.SecurityGroupRuleCreationError(str(e))
def delete_security_group_rule(self, sec_group_rule_id):
try:
self.client.security_group_rules.delete(sec_group_rule_id)
except nova_exceptions.ClientException as e:
LOG.exception('Failed to delete rule to remote security group')
raise exception.SecurityGroupRuleDeletionError(str(e))

View File

@ -20,6 +20,7 @@ import unittest
GROUP = "dbaas.guest"
GROUP_NEUTRON = "dbaas.neutron"
GROUP_START = "dbaas.guest.initialize"
GROUP_START_SIMPLE = "dbaas.guest.initialize.simple"
GROUP_TEST = "dbaas.guest.test"
@ -46,6 +47,7 @@ from proboscis import after_class
from proboscis import test
from proboscis import SkipTest
from proboscis.asserts import assert_equal
from proboscis.asserts import assert_false
from proboscis.asserts import assert_not_equal
from proboscis.asserts import assert_raises
from proboscis.asserts import assert_is_not_none
@ -55,6 +57,7 @@ from proboscis.asserts import fail
from trove import tests
from trove.tests.config import CONFIG
from trove.tests.util import create_dbaas_client
from trove.tests.util import create_nova_client
from trove.tests.util.usage import create_usage_verifier
from trove.tests.util import dns_checker
from trove.tests.util import iso_time
@ -676,6 +679,89 @@ class CreateInstance(object):
check.volume()
@test(depends_on_classes=[InstanceSetup], groups=[GROUP_NEUTRON])
class CreateInstanceWithNeutron(unittest.TestCase):
@time_out(TIMEOUT_INSTANCE_CREATE)
def setUp(self):
if not CONFIG.values.get('neutron_enabled'):
raise SkipTest("neutron is not enabled, skipping")
user = test_config.users.find_user(
Requirements(is_admin=False, services=["nova", "trove"]))
self.nova_client = create_nova_client(user)
self.dbaas_client = create_dbaas_client(user)
self.result = None
self.instance_name = ("TEST_INSTANCE_CREATION_WITH_NICS"
+ str(datetime.now()))
databases = []
self.default_cidr = CONFIG.values.get('shared_network_subnet', None)
if VOLUME_SUPPORT:
volume = {'size': 1}
else:
volume = None
self.result = self.dbaas_client.instances.create(
self.instance_name,
instance_info.dbaas_flavor_href,
volume, databases)
self.instance_id = self.result.id
def verify_instance_is_active():
result = self.dbaas_client.instances.get(self.instance_id)
if result.status == "ACTIVE":
return True
else:
assert_equal("BUILD", result.status)
return False
poll_until(verify_instance_is_active)
def tearDown(self):
if self.result.id is not None:
self.dbaas_client.instances.delete(self.result.id)
while True:
try:
self.dbaas_client.instances.get(self.result.id)
except exceptions.NotFound:
return True
time.sleep(1)
def check_ip_within_network(self, ip, network):
octet_list = str(ip).split(".")
octets, mask = str(network).split("/")
octet_list_ = octets.split(".")
for i in range(int(mask) / 8):
if octet_list[i] != octet_list_[i]:
return False
return True
def test_ip_within_cidr(self):
nova_instance = None
for server in self.nova_client.servers.list():
if str(server.name) == self.instance_name:
nova_instance = server
break
if nova_instance is None:
fail("instance created with neutron enabled is not found in nova")
for address in nova_instance.addresses['private']:
ip = address['addr']
assert_true(self.check_ip_within_network(ip, self.default_cidr))
# black list filtered ip not visible via troveclient
trove_instance = self.dbaas_client.instances.get(self.result.id)
for ip in trove_instance.ip:
if str(ip).startswith('10.'):
assert_true(self.check_ip_within_network(ip, "10.0.0.0/24"))
assert_false(self.check_ip_within_network(ip, "10.0.1.0/24"))
@test(depends_on_classes=[CreateInstance],
groups=[GROUP,
GROUP_START,

View File

@ -289,9 +289,11 @@ class FakeServers(object):
raise nova_exceptions.ClientException("The requested availability "
"zone is not available.")
if nics is not None and nics.port_id == 'UNKNOWN':
raise nova_exceptions.ClientException("The requested availability "
"zone is not available.")
if nics:
if 'port-id' in nics[0] and nics[0]['port-id'] == "UNKNOWN":
raise nova_exceptions.ClientException("The requested "
"port-id is not "
"available.")
server.schedule_status("ACTIVE", 1)
LOG.info(_("FAKE_SERVERS_DB : %s") % str(FAKE_SERVERS_DB))

View File

@ -40,30 +40,35 @@ class SimpleInstanceTest(TestCase):
"public": [{"addr": "15.123.123.123"}]}
self.orig_conf = CONF.network_label_regex
self.orig_ip_regex = CONF.ip_regex
self.orig_black_list_regex = CONF.black_list_regex
def tearDown(self):
super(SimpleInstanceTest, self).tearDown()
CONF.network_label_regex = self.orig_conf
CONF.ip_start = None
CONF.ip_regex = self.orig_ip_regex
def test_get_root_on_create(self):
root_on_create_val = Instance.get_root_on_create('redis')
self.assertFalse(root_on_create_val)
def test_filter_ips(self):
def test_filter_ips_white_list(self):
CONF.network_label_regex = '.*'
CONF.ip_regex = '^(15.|123.)'
CONF.black_list_regex = '^10.123.123.*'
ip = self.instance.get_visible_ip_addresses()
ip = filter_ips(ip, CONF.ip_regex)
ip = filter_ips(ip, CONF.ip_regex, CONF.black_list_regex)
self.assertTrue(len(ip) == 2)
self.assertTrue('123.123.123.123' in ip)
self.assertTrue('15.123.123.123' in ip)
def test_one_network_label_exact(self):
CONF.network_label_regex = '^internal$'
def test_filter_ips_black_list(self):
CONF.network_label_regex = '.*'
CONF.ip_regex = '.*'
CONF.black_list_regex = '^10.123.123.*'
ip = self.instance.get_visible_ip_addresses()
self.assertEqual(['10.123.123.123'], ip)
ip = filter_ips(ip, CONF.ip_regex, CONF.black_list_regex)
self.assertTrue(len(ip) == 2)
self.assertTrue('10.123.123.123' not in ip)
def test_one_network_label(self):
CONF.network_label_regex = 'public'

View File

@ -0,0 +1,115 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# 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 testtools
from mock import MagicMock
from mock import Mock
from neutronclient.common import exceptions as neutron_exceptions
from neutronclient.v2_0 import client as NeutronClient
from trove.common import exception
from trove.common import remote
from trove.common.models import NetworkRemoteModelBase
from trove.network import neutron
from trove.network.neutron import NeutronDriver as driver
from trove.extensions.security_group.models import RemoteSecurityGroup
class NeutronDriverTest(testtools.TestCase):
def setUp(self):
super(NeutronDriverTest, self).setUp()
self.context = Mock()
self.orig_neutron_driver = NetworkRemoteModelBase.get_driver
self.orig_create_sg = driver.create_security_group
self.orig_add_sg_rule = driver.add_security_group_rule
self.orig_del_sg_rule = driver.delete_security_group_rule
self.orig_del_sg = driver.delete_security_group
NetworkRemoteModelBase.get_driver = Mock(return_value=driver)
def tearDown(self):
super(NeutronDriverTest, self).tearDown()
NetworkRemoteModelBase.get_driver = self.orig_neutron_driver
driver.create_security_group = self.orig_create_sg
driver.add_security_group_rule = self.orig_add_sg_rule
driver.delete_security_group_rule = self.orig_del_sg_rule
driver.delete_security_group = self.orig_del_sg
def test_create_security_group(self):
driver.create_security_group = Mock()
RemoteSecurityGroup.create(name=Mock(), description=Mock(),
context=self.context)
self.assertEqual(1, driver.create_security_group.call_count)
def test_add_security_group_rule(self):
driver.add_security_group_rule = Mock()
RemoteSecurityGroup.add_rule(sec_group_id=Mock(), protocol=Mock(),
from_port=Mock(), to_port=Mock(),
cidr=Mock(), context=self.context)
self.assertEqual(1, driver.add_security_group_rule.call_count)
def test_delete_security_group_rule(self):
driver.delete_security_group_rule = Mock()
RemoteSecurityGroup.delete_rule(sec_group_rule_id=Mock(),
context=self.context)
self.assertEqual(1, driver.delete_security_group_rule.call_count)
def test_delete_security_group(self):
driver.delete_security_group = Mock()
RemoteSecurityGroup.delete(sec_group_id=Mock(),
context=self.context)
self.assertEqual(1, driver.delete_security_group.call_count)
class NeutronDriverExceptionTest(testtools.TestCase):
def setUp(self):
super(NeutronDriverExceptionTest, self).setUp()
self.context = Mock()
self.orig_neutron_driver = NetworkRemoteModelBase.get_driver
self.orig_NeutronClient = NeutronClient.Client
self.orig_get_endpoint = remote.get_endpoint
remote.get_endpoint = MagicMock(return_value="neutron_url")
mock_driver = neutron.NeutronDriver(self.context)
NetworkRemoteModelBase.get_driver = MagicMock(
return_value=mock_driver)
NeutronClient.Client = Mock(
side_effect=neutron_exceptions.NeutronClientException())
def tearDown(self):
super(NeutronDriverExceptionTest, self).tearDown()
NetworkRemoteModelBase.get_driver = self.orig_neutron_driver
NeutronClient.Client = self.orig_NeutronClient
remote.get_endpoint = self.orig_get_endpoint
def test_create_sg_with_exception(self):
self.assertRaises(exception.SecurityGroupCreationError,
RemoteSecurityGroup.create,
"sg_name", "sg_desc", self.context)
def test_add_sg_rule_with_exception(self):
self.assertRaises(exception.SecurityGroupRuleCreationError,
RemoteSecurityGroup.add_rule,
"12234", "tcp", "22", "22",
"0.0.0.0/8", self.context)
def test_delete_sg_rule_with_exception(self):
self.assertRaises(exception.SecurityGroupRuleDeletionError,
RemoteSecurityGroup.delete_rule,
"12234", self.context)
def test_delete_sg_with_exception(self):
self.assertRaises(exception.SecurityGroupDeletionError,
RemoteSecurityGroup.delete,
"123445", self.context)