357 lines
13 KiB
Python
357 lines
13 KiB
Python
"""
|
|
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)
|
|
self._create_resource_group_if_not_created(conf)
|
|
return self._network_client
|
|
|
|
def _create_resource_group_if_not_created(self, conf):
|
|
is_resource_created = utils.check_resource_existence(
|
|
self.resource_client, conf.resource_group)
|
|
if not is_resource_created:
|
|
utils.create_resource_group(
|
|
self.resource_client, conf.resource_group, conf.region)
|
|
|
|
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)
|