Merge "Add Azure support for Neutron"
This commit is contained in:
commit
b4369a7e5f
|
@ -0,0 +1,30 @@
|
|||
"""
|
||||
Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 oslo_config import cfg
|
||||
|
||||
azure_group = cfg.OptGroup(name='azure',
|
||||
title='Options to connect to Azure cloud')
|
||||
|
||||
azure_opts = [
|
||||
cfg.StrOpt('tenant_id', help='Tenant id of Azure account'),
|
||||
cfg.StrOpt('client_id', help='Azure client id'),
|
||||
cfg.StrOpt('client_secret', help='Azure Client secret', secret=True),
|
||||
cfg.StrOpt('subscription_id', help='Azure subscription id'),
|
||||
cfg.StrOpt('region', help='Azure region'),
|
||||
cfg.StrOpt('resource_group', help="Azure resource group"),
|
||||
]
|
||||
|
||||
cfg.CONF.register_group(azure_group)
|
||||
cfg.CONF.register_opts(azure_opts, group=azure_group)
|
||||
|
||||
azure_conf = cfg.CONF.azure
|
|
@ -0,0 +1,231 @@
|
|||
"""
|
||||
Copyright (c) 2017 Platform9 Systems Inc.
|
||||
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 expressed or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
"""
|
||||
from functools import partial
|
||||
import uuid
|
||||
|
||||
from azure.common.credentials import ServicePrincipalCredentials
|
||||
from azure.mgmt.compute import ComputeManagementClient
|
||||
from azure.mgmt.network import NetworkManagementClient
|
||||
from msrestazure.azure_exceptions import CloudError
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.extensions import securitygroup as sg
|
||||
from neutron_lib import exceptions as n_exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FloatingIPNotFound(n_exceptions.NotFound):
|
||||
message = "Floating IP %(ip)s could not be found."
|
||||
|
||||
|
||||
def azure_handle_exception(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
except Exception as e:
|
||||
LOG.exception("Exception occurred in Azure operation: %s" %
|
||||
(e.message))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def get_credentials(tenant_id, client_id, client_secret):
|
||||
credentials = ServicePrincipalCredentials(
|
||||
client_id=client_id, secret=client_secret, tenant=tenant_id)
|
||||
return credentials
|
||||
|
||||
|
||||
def _get_client(tenant_id, client_id, client_secret, subscription_id,
|
||||
cls=None):
|
||||
"""Returns Azure compute resource object for interacting with Azure API
|
||||
|
||||
:param tenant_id: string, tenant_id from azure account
|
||||
:param client_id: string, client_id (application id)
|
||||
:param client_secret: string, secret key of application
|
||||
:param subscription_id: string, unique identification id of account
|
||||
:return: :class:`Resource <Resource>` object
|
||||
"""
|
||||
credentials = get_credentials(tenant_id, client_id, client_secret)
|
||||
client = cls(credentials, subscription_id)
|
||||
return client
|
||||
|
||||
|
||||
get_compute_client = partial(_get_client, cls=ComputeManagementClient)
|
||||
get_network_client = partial(_get_client, cls=NetworkManagementClient)
|
||||
|
||||
|
||||
def _perform_and_wait(operation, args=(), kwargs={}, timeout=300):
|
||||
operation(*args, **kwargs).wait(timeout=timeout)
|
||||
|
||||
|
||||
def create_network(network, resource_group, name, info):
|
||||
_perform_and_wait(network.virtual_networks.create_or_update,
|
||||
(resource_group, name, info))
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_network(network, resource_group, name):
|
||||
_perform_and_wait(network.virtual_networks.delete, (resource_group, name))
|
||||
|
||||
|
||||
def get_network(network, resource_group, name):
|
||||
try:
|
||||
return network.virtual_networks.get(resource_group, name)
|
||||
except CloudError:
|
||||
raise n_exceptions.NetworkNotFound(net_id=name)
|
||||
|
||||
|
||||
def create_subnet(network, resource_group, network_name, name, info):
|
||||
_perform_and_wait(network.subnets.create_or_update,
|
||||
(resource_group, network_name, name, info))
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_subnet(network, resource_group, network_name, name):
|
||||
_perform_and_wait(network.subnets.delete, (resource_group, network_name,
|
||||
name))
|
||||
|
||||
|
||||
def get_subnet(network, resource_group, network_name, name):
|
||||
try:
|
||||
return network.subnets.get(resource_group, network_name, name)
|
||||
except CloudError:
|
||||
raise n_exceptions.SubnetNotFound(subnet_id=name)
|
||||
|
||||
|
||||
def get_nic(network, resource_group, name):
|
||||
try:
|
||||
return network.network_interfaces.get(resource_group, name)
|
||||
except CloudError:
|
||||
raise n_exceptions.PortNotFound(port_id=name)
|
||||
|
||||
|
||||
def create_nic(network, resource_group, name, body):
|
||||
_perform_and_wait(network.network_interfaces.create_or_update,
|
||||
(resource_group, name, body))
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_nic(network, resource_group, name):
|
||||
_perform_and_wait(network.network_interfaces.delete, (resource_group,
|
||||
name))
|
||||
|
||||
|
||||
def get_sg(network, resource_group, name):
|
||||
return network.network_security_groups.get(resource_group, name)
|
||||
|
||||
|
||||
def create_sg(network, resource_group, name, body):
|
||||
_perform_and_wait(network.network_security_groups.create_or_update,
|
||||
(resource_group, name, body))
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_sg(network, resource_group, name):
|
||||
_perform_and_wait(network.network_security_groups.delete, (resource_group,
|
||||
name))
|
||||
|
||||
|
||||
def get_sg_rule(network, resource_group, sg_name, name):
|
||||
return network.network_security_rules.get(resource_group, sg_name, name)
|
||||
|
||||
|
||||
def create_sg_rule(network, resource_group, sg_name, name, body):
|
||||
_perform_and_wait(network.security_rules.create_or_update,
|
||||
(resource_group, sg_name, name, body))
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_sg_rule(network, resource_group, sg_name, name):
|
||||
_perform_and_wait(network.security_rules.delete, (resource_group, sg_name,
|
||||
name))
|
||||
|
||||
|
||||
# Maintaining different calls for update to simplify mocking
|
||||
update_network = create_network
|
||||
update_nic = create_nic
|
||||
update_sg = create_sg
|
||||
update_sg_rule = create_sg_rule
|
||||
|
||||
|
||||
def convert_sg_rule(openstack_rule, priority=None):
|
||||
directions = {'ingress': 'Inbound', 'egress': 'Outbound'}
|
||||
protocols = {'tcp': 'Tcp', 'udp': 'Udp'}
|
||||
|
||||
# Asterix '*' is used to match all possible values.
|
||||
# E.g. In case of source_port_range it will allow all ports.
|
||||
# The default security group is allow all traffic, based on
|
||||
# user inputs we refine it further.
|
||||
sg_rule = {
|
||||
'source_port_range': '*',
|
||||
'destination_port_range': '*',
|
||||
'source_address_prefix': '*',
|
||||
'destination_address_prefix': '*',
|
||||
'access': 'Allow',
|
||||
'priority': priority
|
||||
}
|
||||
sg_rule['direction'] = directions[openstack_rule['direction']]
|
||||
|
||||
if openstack_rule['ethertype'] != 'IPv4':
|
||||
raise sg.SecurityGroupRuleInvalidEtherType(
|
||||
ethertype=openstack_rule['ethertype'], values=('IPv4', ))
|
||||
|
||||
protocol = openstack_rule['protocol']
|
||||
if protocol is None:
|
||||
sg_rule['protocol'] = '*'
|
||||
if protocol and protocol in protocols:
|
||||
sg_rule['protocol'] = protocols[protocol]
|
||||
else:
|
||||
raise sg.SecurityGroupRuleInvalidProtocol(
|
||||
protocol=protocol, values=protocols.keys())
|
||||
|
||||
port_range_min = openstack_rule['port_range_min']
|
||||
port_range_max = openstack_rule['port_range_max']
|
||||
if port_range_min and port_range_min == port_range_max:
|
||||
sg_rule['destination_port_range'] = str(port_range_min)
|
||||
elif port_range_min and port_range_max:
|
||||
sg_rule['destination_port_range'] = "%s-%s" % (port_range_min,
|
||||
port_range_max)
|
||||
|
||||
if openstack_rule['remote_ip_prefix']:
|
||||
# TODO(ssudake21): Allow support for tags in source_address_prefix
|
||||
sg_rule['source_address_prefix'] = openstack_rule['remote_ip_prefix']
|
||||
|
||||
return sg_rule
|
||||
|
||||
|
||||
def allocate_floatingip(network, resource_group, region):
|
||||
name = 'eip-' + str(uuid.uuid4())
|
||||
data = {
|
||||
'location': region,
|
||||
'public_ip_allocation_method': 'Static',
|
||||
'public_ip_address_version': 'IPv4',
|
||||
'idle_timeout_in_minutes': 4
|
||||
}
|
||||
response = network.public_ip_addresses.create_or_update(
|
||||
resource_group, name, data)
|
||||
return response.result()
|
||||
|
||||
|
||||
def get_floatingip(network, resource_group, ip):
|
||||
for public_ip in network.public_ip_addresses.list(resource_group):
|
||||
if public_ip.ip_address == ip:
|
||||
return public_ip
|
||||
raise FloatingIPNotFound(ip=ip)
|
||||
|
||||
|
||||
@azure_handle_exception
|
||||
def delete_floatingip(network, resource_group, public_ip_name):
|
||||
_perform_and_wait(network.public_ip_addresses.delete, (resource_group,
|
||||
public_ip_name))
|
|
@ -0,0 +1,349 @@
|
|||
"""
|
||||
Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 random
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
import ipaddr
|
||||
from neutron.callbacks import events
|
||||
from neutron.callbacks import registry
|
||||
from neutron.callbacks import resources
|
||||
from neutron.common.azure.config import azure_conf
|
||||
from neutron.common.azure import utils
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib import exceptions as e
|
||||
|
||||
try:
|
||||
from neutron_lib.plugins import directory
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def log_network_hook(function):
|
||||
def wraps(self, context):
|
||||
LOG.debug("Called %s with context: %s" % (function.__name__, context))
|
||||
return function(self, context)
|
||||
|
||||
return wraps
|
||||
|
||||
|
||||
# Custom VIF type for Azure
|
||||
VIF_TYPE_AZURE = "azure_nic"
|
||||
|
||||
|
||||
class AzureMechanismDriver(api.MechanismDriver):
|
||||
"""Ml2 Mechanism driver for Azure"""
|
||||
|
||||
def __init__(self):
|
||||
super(AzureMechanismDriver, self).__init__()
|
||||
self._network_client = None
|
||||
self._sg_callback_map = {}
|
||||
|
||||
def initialize(self):
|
||||
LOG.info("Azure Mechanism driver init with %s project, %s region" %
|
||||
(azure_conf.tenant_id, azure_conf.region))
|
||||
self._subscribe_events()
|
||||
|
||||
def _subscribe_events(self):
|
||||
events_info = [(resources.SECURITY_GROUP, events.BEFORE_DELETE,
|
||||
self._delete_secgrp),
|
||||
(resources.SECURITY_GROUP, events.BEFORE_UPDATE,
|
||||
self._update_secgrp), (resources.SECURITY_GROUP,
|
||||
events.BEFORE_CREATE,
|
||||
self._validate_secgrp),
|
||||
(resources.SECURITY_GROUP, events.AFTER_CREATE,
|
||||
self._create_secgrp), (resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_DELETE,
|
||||
self._delete_secrule),
|
||||
(resources.SECURITY_GROUP_RULE, events.BEFORE_UPDATE,
|
||||
self._update_secrule), (resources.SECURITY_GROUP_RULE,
|
||||
events.BEFORE_CREATE,
|
||||
self._validate_secrule),
|
||||
(resources.SECURITY_GROUP_RULE, events.AFTER_CREATE,
|
||||
self._create_secrule)]
|
||||
for resource, event, callback in events_info:
|
||||
registry.subscribe(self.secgrp_callback, resource, event)
|
||||
self._sg_callback_map[(resource, event)] = callback
|
||||
LOG.info("Azure mechanism driver registered security groups callbacks")
|
||||
|
||||
@property
|
||||
def network_client(self):
|
||||
conf = azure_conf
|
||||
if self._network_client is None:
|
||||
args = (conf.tenant_id, conf.client_id, conf.client_secret,
|
||||
conf.subscription_id)
|
||||
self._network_client = utils.get_network_client(*args)
|
||||
return self._network_client
|
||||
|
||||
def _azure_network_name(self, context):
|
||||
return 'net-' + context.current[api.ID]
|
||||
|
||||
def _azure_subnet_name(self, context):
|
||||
return 'subnet-' + context.current[api.ID]
|
||||
|
||||
def _azure_subnet_network_name(self, context):
|
||||
return 'net-' + context.current['network_id']
|
||||
|
||||
def _azure_secgrp_id(self, openstack_id):
|
||||
return "secgrp-" + openstack_id
|
||||
|
||||
def _azure_secrule_id(self, openstack_id):
|
||||
return "secrule-" + openstack_id
|
||||
|
||||
@staticmethod
|
||||
def is_private_network(cidr):
|
||||
return ipaddr.IPNetwork(cidr).is_private
|
||||
|
||||
@log_network_hook
|
||||
def create_network_precommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def create_network_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_network_precommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_network_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def delete_network_precommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def delete_network_postcommit(self, context):
|
||||
name = self._azure_network_name(context)
|
||||
utils.delete_network(self.network_client, azure_conf.resource_group,
|
||||
name)
|
||||
|
||||
@log_network_hook
|
||||
def create_subnet_precommit(self, context):
|
||||
net_svc = self.network_client
|
||||
name = self._azure_subnet_name(context)
|
||||
network_name = self._azure_subnet_network_name(context)
|
||||
cidr = context.current['cidr']
|
||||
if self.is_private_network(cidr):
|
||||
try:
|
||||
azure_network = utils.get_network(
|
||||
net_svc, azure_conf.resource_group, network_name)
|
||||
address_prefixes = azure_network.address_space.address_prefixes
|
||||
if cidr not in address_prefixes:
|
||||
address_prefixes.append(cidr)
|
||||
utils.update_network(net_svc, azure_conf.resource_group,
|
||||
network_name, azure_network)
|
||||
except e.NetworkNotFound:
|
||||
body = {
|
||||
'location': azure_conf.region,
|
||||
'address_space': {
|
||||
'address_prefixes': [
|
||||
cidr,
|
||||
]
|
||||
}
|
||||
}
|
||||
utils.create_network(net_svc, azure_conf.resource_group,
|
||||
network_name, body)
|
||||
utils.create_subnet(net_svc, azure_conf.resource_group,
|
||||
network_name, name, {'address_prefix': cidr})
|
||||
|
||||
@log_network_hook
|
||||
def create_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_subnet_precommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def delete_subnet_precommit(self, context):
|
||||
cidr = context.current['cidr']
|
||||
if self.is_private_network(cidr):
|
||||
name = self._azure_subnet_name(context)
|
||||
network_name = self._azure_subnet_network_name(context)
|
||||
utils.delete_subnet(self.network_client, azure_conf.resource_group,
|
||||
network_name, name)
|
||||
|
||||
@log_network_hook
|
||||
def delete_subnet_postcommit(self, context):
|
||||
pass
|
||||
|
||||
def _check_dev_owner(self, port_context):
|
||||
dev_owner = port_context.current['device_owner']
|
||||
return len(dev_owner) == 0 or dev_owner.startswith(
|
||||
n_const.DEVICE_OWNER_COMPUTE_PREFIX)
|
||||
|
||||
@log_network_hook
|
||||
def create_port_precommit(self, context):
|
||||
LOG.debug("Create_port_precommit: %s" % context.current)
|
||||
if not self._check_dev_owner(context):
|
||||
return
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
region = azure_conf.region
|
||||
network_name = self._azure_subnet_network_name(context)
|
||||
details = context.current['fixed_ips'][0]
|
||||
subnet_name = 'subnet-' + details['subnet_id']
|
||||
ip_address = details['ip_address']
|
||||
nic_name = 'nic-' + context.current['id']
|
||||
ipc_name = 'ipc-' + context.current['id']
|
||||
azure_subnet = utils.get_subnet(net_svc, resource_group, network_name,
|
||||
subnet_name)
|
||||
body = {
|
||||
'location':
|
||||
region,
|
||||
'ip_configurations': [{
|
||||
'name': ipc_name,
|
||||
'private_ip_address': ip_address,
|
||||
'private_ip_allocation_method': 'Static',
|
||||
'subnet': {
|
||||
'id': azure_subnet.id
|
||||
},
|
||||
}]
|
||||
}
|
||||
security_groups = context.current['security_groups']
|
||||
if security_groups and len(security_groups) == 1:
|
||||
sg_name = self._azure_secgrp_id(security_groups[0])
|
||||
sg = utils.get_sg(net_svc, resource_group, sg_name)
|
||||
body['network_security_group'] = {'id': sg.id}
|
||||
utils.create_nic(net_svc, resource_group, nic_name, body)
|
||||
LOG.info("Created NIC %s on Azure." % nic_name)
|
||||
|
||||
@log_network_hook
|
||||
def create_port_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_port_precommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def update_port_postcommit(self, context):
|
||||
pass
|
||||
|
||||
@log_network_hook
|
||||
def delete_port_precommit(self, context):
|
||||
if not self._check_dev_owner(context):
|
||||
return
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
nic_name = 'nic-' + context.current['id']
|
||||
utils.get_nic(net_svc, resource_group, nic_name)
|
||||
utils.delete_nic(net_svc, resource_group, nic_name)
|
||||
LOG.info("Deleted NIC %s on Azure." % nic_name)
|
||||
|
||||
@log_network_hook
|
||||
def delete_port_postcommit(self, context):
|
||||
pass
|
||||
|
||||
def get_secgrp(self, context, id):
|
||||
try:
|
||||
core_plugin = NeutronManager.get_plugin()
|
||||
except AttributeError:
|
||||
core_plugin = directory.get_plugin()
|
||||
return core_plugin.get_security_group(context, id)
|
||||
|
||||
def get_secgrp_rule(self, context, id):
|
||||
try:
|
||||
core_plugin = NeutronManager.get_plugin()
|
||||
except AttributeError:
|
||||
core_plugin = directory.get_plugin()
|
||||
return core_plugin.get_security_group_rule(context, id)
|
||||
|
||||
def _validate_secrule(self, **kwargs):
|
||||
rule = kwargs['security_group_rule']
|
||||
utils.convert_sg_rule(rule)
|
||||
|
||||
def _create_secrule(self, **kwargs):
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
rule = kwargs['security_group_rule']
|
||||
azure_rule = utils.convert_sg_rule(rule)
|
||||
sg_name = self._azure_secgrp_id(rule['security_group_id'])
|
||||
name = self._azure_secrule_id(rule['id'])
|
||||
sg = utils.get_sg(net_svc, resource_group, sg_name)
|
||||
"""Each Azure security rule has a priority.
|
||||
The value can be between 100 and 4096. The priority number must be
|
||||
unique for each rule in the collection. The lower the priority number,
|
||||
the higher the priority of the rule.
|
||||
"""
|
||||
previous_priorities = sorted([i.priority for i in sg.security_rules])
|
||||
if previous_priorities:
|
||||
priority = previous_priorities[-1] + 1
|
||||
else:
|
||||
priority = 100
|
||||
azure_rule['priority'] = priority
|
||||
utils.create_sg_rule(net_svc, resource_group, sg_name, name,
|
||||
azure_rule)
|
||||
|
||||
def _update_secrule(self, **kwargs):
|
||||
pass
|
||||
|
||||
def _delete_secrule(self, **kwargs):
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
secrule_id = kwargs['security_group_rule_id']
|
||||
sec_rule = self.get_secgrp_rule(kwargs['context'], secrule_id)
|
||||
sg_name = self._azure_secgrp_id(sec_rule['security_group_id'])
|
||||
name = self._azure_secrule_id(secrule_id)
|
||||
utils.delete_sg_rule(net_svc, resource_group, sg_name, name)
|
||||
|
||||
def _validate_secgrp(self, **kwargs):
|
||||
pass
|
||||
|
||||
def _create_secgrp(self, **kwargs):
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
region = azure_conf.region
|
||||
name = self._azure_secgrp_id(kwargs['security_group']['id'])
|
||||
body = {
|
||||
'location': region,
|
||||
}
|
||||
utils.create_sg(net_svc, resource_group, name, body)
|
||||
|
||||
def _update_secgrp(self, **kwargs):
|
||||
pass
|
||||
|
||||
def _delete_secgrp(self, **kwargs):
|
||||
net_svc = self.network_client
|
||||
resource_group = azure_conf.resource_group
|
||||
name = self._azure_secgrp_id(kwargs['security_group_id'])
|
||||
utils.delete_sg(net_svc, resource_group, name)
|
||||
|
||||
@log_network_hook
|
||||
def bind_port(self, context):
|
||||
fixed_ip_dict = dict()
|
||||
if 'fixed_ips' in context.current:
|
||||
if len(context.current['fixed_ips']):
|
||||
fixed_ip_dict = context.current['fixed_ips'][0]
|
||||
|
||||
segment_id = random.choice(context.segments_to_bind)[api.ID]
|
||||
context.set_binding(
|
||||
segment_id, VIF_TYPE_AZURE, fixed_ip_dict, status='ACTIVE')
|
||||
return True
|
||||
|
||||
def secgrp_callback(self, resource, event, trigger, **kwargs):
|
||||
LOG.info("Secgrp_callback: %s %s %s" % (resource, event, kwargs))
|
||||
callback = self._sg_callback_map[(resource, event)]
|
||||
callback(**kwargs)
|
|
@ -0,0 +1,203 @@
|
|||
"""
|
||||
Copyright 2017 Platform9 Systems Inc.(http://www.platform9.com)
|
||||
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 neutron_lib
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
from neutron.common.azure.config import azure_conf
|
||||
from neutron.common.azure import utils
|
||||
from neutron.db import common_db_mixin
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import l3_dvrscheduler_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import l3_hamode_db
|
||||
from neutron.db import l3_hascheduler_db
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.quota import resource_registry
|
||||
from neutron.services import service_base
|
||||
from neutron_lib import constants as n_const
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
if LooseVersion(neutron_lib.__version__) < LooseVersion("1.0.0"):
|
||||
router = l3_db.Router
|
||||
floating_ip = l3_db.FloatingIP
|
||||
plugin_type = constants.L3_ROUTER_NAT
|
||||
service_plugin_class = service_base.ServicePluginBase
|
||||
else:
|
||||
from neutron.db.models import l3
|
||||
from neutron_lib.plugins import constants as plugin_constants
|
||||
from neutron_lib.services import base
|
||||
router = l3.Router
|
||||
floating_ip = l3.FloatingIP
|
||||
plugin_type = plugin_constants.L3
|
||||
service_plugin_class = base.ServicePluginBase
|
||||
|
||||
|
||||
class AzureRouterPlugin(
|
||||
service_plugin_class, common_db_mixin.CommonDbMixin,
|
||||
extraroute_db.ExtraRoute_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin, l3_dvrscheduler_db.L3_DVRsch_db_mixin,
|
||||
l3_hascheduler_db.L3_HA_scheduler_db_mixin):
|
||||
"""Implementation of the Neutron L3 Router Service Plugin.
|
||||
|
||||
This class implements a L3 service plugin that provides
|
||||
router and floatingip resources and manages associated
|
||||
request/response.
|
||||
All DB related work is implemented in classes
|
||||
l3_db.L3_NAT_db_mixin, l3_hamode_db.L3_HA_NAT_db_mixin,
|
||||
l3_dvr_db.L3_NAT_with_dvr_db_mixin, and extraroute_db.ExtraRoute_db_mixin.
|
||||
"""
|
||||
supported_extension_aliases = [
|
||||
"dvr", "router", "ext-gw-mode", "extraroute", "l3_agent_scheduler",
|
||||
"l3-ha"
|
||||
]
|
||||
|
||||
@resource_registry.tracked_resources(router=router, floatingip=floating_ip)
|
||||
def __init__(self):
|
||||
super(AzureRouterPlugin, self).__init__()
|
||||
l3_db.subscribe()
|
||||
self._compute_client = None
|
||||
self._network_client = None
|
||||
self.tenant_id = azure_conf.tenant_id
|
||||
self.client_id = azure_conf.client_id
|
||||
self.client_secret = azure_conf.client_secret
|
||||
self.subscription_id = azure_conf.subscription_id
|
||||
self.region = azure_conf.region
|
||||
self.resource_group = azure_conf.resource_group
|
||||
|
||||
LOG.info("Azure Router plugin init with %s project, %s region" %
|
||||
(self.tenant_id, self.region))
|
||||
|
||||
@property
|
||||
def compute_client(self):
|
||||
if self._compute_client is None:
|
||||
args = (self.tenant_id, self.client_id, self.client_secret,
|
||||
self.subscription_id)
|
||||
self._compute_client = utils.get_compute_client(*args)
|
||||
return self._compute_client
|
||||
|
||||
@property
|
||||
def network_client(self):
|
||||
if self._network_client is None:
|
||||
args = (self.tenant_id, self.client_id, self.client_secret,
|
||||
self.subscription_id)
|
||||
self._network_client = utils.get_network_client(*args)
|
||||
return self._network_client
|
||||
|
||||
def get_plugin_type(self):
|
||||
return plugin_type
|
||||
|
||||
def get_plugin_description(self):
|
||||
"""returns string description of the plugin."""
|
||||
return ("Azure L3 Router Service Plugin for basic L3 forwarding"
|
||||
" between (L2) Neutron networks and access to external"
|
||||
" networks via a NAT gateway.")
|
||||
|
||||
def _add_floatingip_to_port(self, port_id, public_ip):
|
||||
net_svc = self.network_client
|
||||
resource_group = self.resource_group
|
||||
nic_name = 'nic-' + port_id
|
||||
nic = utils.get_nic(net_svc, resource_group, nic_name)
|
||||
nic.ip_configurations[0].public_ip_address = public_ip
|
||||
utils.update_nic(net_svc, resource_group, nic_name, nic)
|
||||
|
||||
def _remove_floatingip_from_port(self, port_id):
|
||||
self._add_floatingip_to_port(port_id, public_ip=None)
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
net_svc = self.network_client
|
||||
resource_group = self.resource_group
|
||||
public_ip = None
|
||||
port_id = None
|
||||
status = n_const.FLOATINGIP_STATUS_DOWN
|
||||
try:
|
||||
floatingip_dict = floatingip['floatingip']
|
||||
public_ip = utils.allocate_floatingip(net_svc, resource_group,
|
||||
self.region)
|
||||
LOG.info("Created Azure static IP %s" % public_ip.ip_address)
|
||||
floatingip_dict['floating_ip_address'] = public_ip.ip_address
|
||||
port_id = floatingip_dict['port_id']
|
||||
if port_id:
|
||||
self._add_floatingip_to_port(port_id, public_ip)
|
||||
status = n_const.FLOATINGIP_STATUS_ACTIVE
|
||||
res = super(AzureRouterPlugin, self).create_floatingip(
|
||||
context, floatingip, initial_status=status)
|
||||
except Exception as e:
|
||||
LOG.exception("Error in Creation/Allocating floating IP: %s" % e)
|
||||
if status == n_const.FLOATINGIP_STATUS_ACTIVE:
|
||||
self._remove_floatingip_from_port(port_id)
|
||||
if public_ip:
|
||||
utils.delete_floatingip(net_svc, resource_group,
|
||||
public_ip.name)
|
||||
raise e
|
||||
return res
|
||||
|
||||
def update_floatingip(self, context, id, floatingip):
|
||||
net_svc = self.network_client
|
||||
resource_group = self.resource_group
|
||||
status = n_const.FLOATINGIP_STATUS_DOWN
|
||||
floatingip_dict = floatingip['floatingip']
|
||||
orig_floatingip = super(AzureRouterPlugin, self).get_floatingip(
|
||||
context, id)
|
||||
ip_address = orig_floatingip['floating_ip_address']
|
||||
port_id = floatingip_dict['port_id']
|
||||
if port_id:
|
||||
public_ip = utils.get_floatingip(net_svc, resource_group,
|
||||
ip_address)
|
||||
self._add_floatingip_to_port(port_id, public_ip)
|
||||
status = n_const.FLOATINGIP_STATUS_ACTIVE
|
||||
else:
|
||||
self._remove_floatingip_from_port(orig_floatingip['port_id'])
|
||||
floatingip_dict['status'] = status
|
||||
return super(AzureRouterPlugin, self).update_floatingip(
|
||||
context, id, floatingip)
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
net_svc = self.network_client
|
||||
resource_group = self.resource_group
|
||||
floating_ip = super(AzureRouterPlugin, self).get_floatingip(
|
||||
context, id)
|
||||
ip_address = floating_ip['floating_ip_address']
|
||||
public_ip = utils.get_floatingip(net_svc, resource_group, ip_address)
|
||||
port_id = floating_ip['port_id']
|
||||
if port_id:
|
||||
self._remove_floatingip_from_port(port_id)
|
||||
utils.delete_floatingip(net_svc, resource_group, public_ip.name)
|
||||
return super(AzureRouterPlugin, self).delete_floatingip(context, id)
|
||||
|
||||
def create_router(self, context, router):
|
||||
LOG.debug("Creating router %s" % router['router']['name'])
|
||||
return super(AzureRouterPlugin, self).create_router(context, router)
|
||||
|
||||
def delete_router(self, context, id):
|
||||
LOG.debug("Deleting router %s" % id)
|
||||
return super(AzureRouterPlugin, self).delete_router(context, id)
|
||||
|
||||
def update_router(self, context, id, router):
|
||||
LOG.debug("Updating router %s" % id)
|
||||
return super(AzureRouterPlugin, self).update_router(
|
||||
context, id, router)
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug("Adding interface %s to router %s" % (interface_info,
|
||||
router_id))
|
||||
return super(AzureRouterPlugin, self).add_router_interface(
|
||||
context, router_id, interface_info)
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
LOG.debug("Deleting interface %s from router %s" % (interface_info,
|
||||
router_id))
|
||||
return super(AzureRouterPlugin, self).remove_router_interface(
|
||||
context, router_id, interface_info)
|
Loading…
Reference in New Issue