Merge "Add neutron support"
This commit is contained in:
commit
c68fef2b7a
@ -60,6 +60,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,
|
||||
@ -75,6 +76,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
|
||||
@ -119,6 +122,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
|
||||
@ -132,8 +139,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/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -18,6 +18,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
|
||||
|
@ -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.'),
|
||||
@ -214,6 +217,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',
|
||||
@ -240,6 +245,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',
|
||||
@ -259,18 +265,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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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():
|
||||
|
||||
|
0
trove/network/__init__.py
Normal file
0
trove/network/__init__.py
Normal file
51
trove/network/base.py
Normal file
51
trove/network/base.py
Normal 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
143
trove/network/neutron.py
Normal 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
80
trove/network/nova.py
Normal 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))
|
@ -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
|
||||
@ -677,6 +680,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,
|
||||
|
@ -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))
|
||||
|
@ -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'
|
||||
|
0
trove/tests/unittests/network/__init__.py
Normal file
0
trove/tests/unittests/network/__init__.py
Normal file
115
trove/tests/unittests/network/test_neutron_driver.py
Normal file
115
trove/tests/unittests/network/test_neutron_driver.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user