Use code from AdvNetworking for MS SQL Cluster IP validation.
Use code borrowed from murano-conductor for generation of subnets against which both MS SQL Cluster IPs should be validated (they should belong to that subnet). Old validation of static IPs is no longer sufficient because CIDR's are generated by murano-conductor during deployment. Valid subnet, once generated in dashboard for validation purposes, is stored in murano-api environment for later use by murano-conductor (DRY). So this commit comes in a team with 2 following commits: https://review.openstack.org/#/c/59982/ and https://review.openstack.org/#/c/59983/ Change-Id: I02c1b1905dd15535f807bcbc49b4f8fc2ad435d7
This commit is contained in:
parent
75b8d9a5f0
commit
7492333859
|
@ -207,8 +207,9 @@ def environment_deploy(request, environment_id):
|
||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def environment_update(request, environment_id, name):
|
def environment_update(request, environment_id, name, **kwargs):
|
||||||
return muranoclient(request).environments.update(environment_id, name)
|
return muranoclient(request).environments.update(
|
||||||
|
environment_id, name, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_environment_name(request, environment_id):
|
def get_environment_name(request, environment_id):
|
||||||
|
|
|
@ -16,9 +16,10 @@ import re
|
||||||
import json
|
import json
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import RegexValidator, validate_ipv4_address
|
from django.core.validators import RegexValidator, validate_ipv4_address
|
||||||
from netaddr import all_matching_cidrs
|
from netaddr import all_matching_cidrs, IPNetwork, IPAddress
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils.encoding import smart_text
|
from django.utils.encoding import smart_text
|
||||||
|
from django.conf import settings
|
||||||
from muranodashboard.environments import api
|
from muranodashboard.environments import api
|
||||||
from horizon import exceptions, messages
|
from horizon import exceptions, messages
|
||||||
from openstack_dashboard.api import glance
|
from openstack_dashboard.api import glance
|
||||||
|
@ -32,6 +33,8 @@ import horizon.tables as tables
|
||||||
import floppyforms
|
import floppyforms
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
|
from .network import NeutronSubnetGetter
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,6 +56,10 @@ def with_request(func):
|
||||||
"""
|
"""
|
||||||
def update(self, initial, request=None, **kwargs):
|
def update(self, initial, request=None, **kwargs):
|
||||||
initial_request = initial.get('request')
|
initial_request = initial.get('request')
|
||||||
|
for key, value in initial.iteritems():
|
||||||
|
if key != 'request' and key not in kwargs:
|
||||||
|
kwargs[key] = value
|
||||||
|
|
||||||
if initial_request:
|
if initial_request:
|
||||||
log.debug("Using 'request' value from initial dictionary")
|
log.debug("Using 'request' value from initial dictionary")
|
||||||
func(self, initial_request, **kwargs)
|
func(self, initial_request, **kwargs)
|
||||||
|
@ -548,8 +555,12 @@ class BooleanField(forms.BooleanField, CustomPropertiesField):
|
||||||
|
|
||||||
|
|
||||||
class ClusterIPField(CharField):
|
class ClusterIPField(CharField):
|
||||||
|
existing_subnet = None
|
||||||
|
network_topology = None
|
||||||
|
router_id = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_cluster_ip(request, ip_ranges):
|
def make_nova_validator(request, ip_ranges):
|
||||||
def perform_checking(ip):
|
def perform_checking(ip):
|
||||||
validate_ipv4_address(ip)
|
validate_ipv4_address(ip)
|
||||||
if not all_matching_cidrs(ip, ip_ranges) and ip_ranges:
|
if not all_matching_cidrs(ip, ip_ranges) and ip_ranges:
|
||||||
|
@ -576,19 +587,64 @@ class ClusterIPField(CharField):
|
||||||
_('Specified Cluster Static IP is already in use'))
|
_('Specified Cluster Static IP is already in use'))
|
||||||
return perform_checking
|
return perform_checking
|
||||||
|
|
||||||
|
def update_network_params(self, request, environment_id):
|
||||||
|
env = api.environment_get(request, environment_id)
|
||||||
|
net_info = getattr(env, 'networking', {})
|
||||||
|
self.network_topology = getattr(settings, 'NETWORK_TOPOLOGY', 'routed')
|
||||||
|
|
||||||
|
if net_info:
|
||||||
|
self.existing_subnet = net_info.get('cidr')
|
||||||
|
topology = net_info.get('topology')
|
||||||
|
if topology:
|
||||||
|
self.network_topology = topology
|
||||||
|
self.router_id = net_info.get('routerId')
|
||||||
|
|
||||||
|
if self.network_topology != 'nova' and not self.existing_subnet:
|
||||||
|
getter = NeutronSubnetGetter(request.user.tenant_id,
|
||||||
|
self.router_id,
|
||||||
|
request.user.token.id)
|
||||||
|
self.existing_subnet = getter.get_subnet(environment_id)
|
||||||
|
if self.existing_subnet:
|
||||||
|
networking = {
|
||||||
|
'createNetwork': True,
|
||||||
|
'cidr': self.existing_subnet
|
||||||
|
}
|
||||||
|
name = api.get_environment_name(request, environment_id)
|
||||||
|
api.environment_update(request, environment_id, name,
|
||||||
|
networking=networking)
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Cannot get subnet')
|
||||||
|
|
||||||
|
def make_neutron_validator(self):
|
||||||
|
def perform_checking(ip):
|
||||||
|
validate_ipv4_address(ip)
|
||||||
|
if not IPAddress(ip) in IPNetwork(self.existing_subnet):
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Specified IP address should belong to {0} '
|
||||||
|
'subnet'.format(self.existing_subnet)))
|
||||||
|
|
||||||
|
return perform_checking
|
||||||
|
|
||||||
@with_request
|
@with_request
|
||||||
def update(self, request, **kwargs):
|
def update(self, request, environment_id, **kwargs):
|
||||||
try:
|
self.update_network_params(request, environment_id)
|
||||||
network_list = novaclient(request).networks.list()
|
|
||||||
ip_ranges = [network.cidr for network in network_list]
|
if self.network_topology == 'nova':
|
||||||
ranges = ', '.join(ip_ranges)
|
try:
|
||||||
except StandardError:
|
network_list = novaclient(request).networks.list()
|
||||||
ip_ranges, ranges = [], ''
|
ip_ranges = [network.cidr for network in network_list]
|
||||||
if ip_ranges:
|
ranges = ', '.join(ip_ranges)
|
||||||
self.help_text = _('Select IP from available range: ' + ranges)
|
except StandardError:
|
||||||
else:
|
ip_ranges, ranges = [], ''
|
||||||
self.help_text = _('Specify valid fixed IP')
|
if ip_ranges:
|
||||||
self.validators = [self.validate_cluster_ip(request, ip_ranges)]
|
self.help_text = _('Select IP from available range: ' + ranges)
|
||||||
|
else:
|
||||||
|
self.help_text = _('Specify valid fixed IP')
|
||||||
|
self.validators = [self.make_nova_validator(request, ip_ranges)]
|
||||||
|
elif self.network_topology == 'routed':
|
||||||
|
self.validators = [self.make_neutron_validator()]
|
||||||
|
else: # 'flat' topology
|
||||||
|
raise NotImplementedError('Flat topology is not implemented yet')
|
||||||
self.error_messages['invalid'] = validate_ipv4_address.message
|
self.error_messages['invalid'] = validate_ipv4_address.message
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
# Copyright (c) 2013 Mirantis 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 express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import math
|
||||||
|
from django.conf import settings
|
||||||
|
from keystoneclient.v2_0 import client as ksclient
|
||||||
|
import netaddr
|
||||||
|
from netaddr.strategy import ipv4
|
||||||
|
from neutronclient.v2_0 import client as neutronclient
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronSubnetGetter(object):
|
||||||
|
def __init__(self, tenant_id, router_id, token):
|
||||||
|
conf = settings.ADVANCED_NETWORKING_CONFIG
|
||||||
|
self.env_count = conf.get('max_environments')
|
||||||
|
self.host_count = conf.get('max_hosts')
|
||||||
|
self.address = conf.get('env_ip_template')
|
||||||
|
|
||||||
|
self.tenant_id = tenant_id
|
||||||
|
self.router_id = router_id
|
||||||
|
|
||||||
|
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||||
|
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||||
|
endpoint_type = getattr(
|
||||||
|
settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')
|
||||||
|
|
||||||
|
keystone_client = ksclient.Client(
|
||||||
|
auth_url=settings.OPENSTACK_KEYSTONE_URL,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
token=token,
|
||||||
|
cacert=cacert,
|
||||||
|
insecure=insecure)
|
||||||
|
|
||||||
|
if not keystone_client.authenticate():
|
||||||
|
raise ksclient.exceptions.Unauthorized()
|
||||||
|
|
||||||
|
neutron_url = keystone_client.service_catalog.url_for(
|
||||||
|
service_type='network', endpoint_type=endpoint_type)
|
||||||
|
self.neutron = neutronclient.Client(endpoint_url=neutron_url,
|
||||||
|
token=token,
|
||||||
|
ca_cert=cacert,
|
||||||
|
insecure=insecure)
|
||||||
|
|
||||||
|
def _get_router_id(self):
|
||||||
|
routers = self.neutron.list_routers(tenant_id=self.tenant_id).\
|
||||||
|
get("routers")
|
||||||
|
if not len(routers):
|
||||||
|
router_id = None
|
||||||
|
else:
|
||||||
|
router_id = routers[0]["id"]
|
||||||
|
|
||||||
|
if len(routers) > 1:
|
||||||
|
for router in routers:
|
||||||
|
if "murano" in router["name"].lower():
|
||||||
|
router_id = router["id"]
|
||||||
|
break
|
||||||
|
|
||||||
|
return router_id
|
||||||
|
|
||||||
|
def _get_subnet(self, router_id=None, count=1):
|
||||||
|
if router_id:
|
||||||
|
taken_cidrs = self._get_taken_cidrs_by_router(router_id)
|
||||||
|
else:
|
||||||
|
taken_cidrs = self._get_all_taken_cidrs()
|
||||||
|
results = []
|
||||||
|
for i in range(0, count):
|
||||||
|
res = self._generate_cidr(taken_cidrs)
|
||||||
|
results.append(res)
|
||||||
|
taken_cidrs.append(res)
|
||||||
|
return results
|
||||||
|
|
||||||
|
def _get_taken_cidrs_by_router(self, router_id):
|
||||||
|
ports = self.neutron.list_ports(device_id=router_id)["ports"]
|
||||||
|
subnet_ids = []
|
||||||
|
for port in ports:
|
||||||
|
for fixed_ip in port["fixed_ips"]:
|
||||||
|
subnet_ids.append(fixed_ip["subnet_id"])
|
||||||
|
|
||||||
|
all_subnets = self.neutron.list_subnets()["subnets"]
|
||||||
|
filtered_cidrs = [subnet["cidr"] for subnet in all_subnets if
|
||||||
|
subnet["id"] in subnet_ids]
|
||||||
|
|
||||||
|
return filtered_cidrs
|
||||||
|
|
||||||
|
def _get_all_taken_cidrs(self):
|
||||||
|
return [subnet["cidr"] for subnet in
|
||||||
|
self.neutron.list_subnets()["subnets"]]
|
||||||
|
|
||||||
|
def _generate_cidr(self, taken_cidrs):
|
||||||
|
bits_for_envs = int(math.ceil(math.log(self.env_count, 2)))
|
||||||
|
bits_for_hosts = int(math.ceil(math.log(self.host_count, 2)))
|
||||||
|
width = ipv4.width
|
||||||
|
mask_width = width - bits_for_hosts - bits_for_envs
|
||||||
|
net = netaddr.IPNetwork(self.address + "/" + str(mask_width))
|
||||||
|
for subnet in net.subnet(width - bits_for_hosts):
|
||||||
|
if str(subnet) in taken_cidrs:
|
||||||
|
continue
|
||||||
|
return str(subnet)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_subnet(self, environment_id):
|
||||||
|
# TODO: should use it for getting cidr in future
|
||||||
|
assert environment_id
|
||||||
|
router_id = self.router_id or self._get_router_id()
|
||||||
|
return self._get_subnet(router_id)[0]
|
|
@ -132,7 +132,10 @@ class Wizard(ModalFormMixin, LazyWizard):
|
||||||
def get_form_initial(self, step):
|
def get_form_initial(self, step):
|
||||||
init_dict = {}
|
init_dict = {}
|
||||||
if step != 'service_choice':
|
if step != 'service_choice':
|
||||||
init_dict['request'] = self.request
|
init_dict.update({
|
||||||
|
'request': self.request,
|
||||||
|
'environment_id': self.kwargs.get('environment_id')
|
||||||
|
})
|
||||||
return self.initial_dict.get(step, init_dict)
|
return self.initial_dict.get(step, init_dict)
|
||||||
|
|
||||||
def get_context_data(self, form, **kwargs):
|
def get_context_data(self, form, **kwargs):
|
||||||
|
|
|
@ -158,3 +158,12 @@ except ImportError:
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
ADVANCED_NETWORKING_CONFIG = {
|
||||||
|
# Maximum number of environments that can be processed simultaneously
|
||||||
|
'max_environments': 100,
|
||||||
|
# Maximum number of VMs per environment
|
||||||
|
'max_hosts': 250,
|
||||||
|
# Template IP address for generating environment subnet cidrs
|
||||||
|
'env_ip_template': '10.0.0.0'
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,8 @@ LOGGING['loggers']['muranoclient'] = {'handlers': ['murano-file'], 'level': 'ERR
|
||||||
#MURANO_METADATA_URL = "http://localhost:8084/v1"
|
#MURANO_METADATA_URL = "http://localhost:8084/v1"
|
||||||
#if murano-api set up with ssl uncomment next strings
|
#if murano-api set up with ssl uncomment next strings
|
||||||
#MURANO_API_INSECURE = True
|
#MURANO_API_INSECURE = True
|
||||||
|
ADVANCED_NETWORKING_CONFIG = {'max_environments': 100, 'max_hosts': 250, 'env_ip_template': '10.0.0.0'}
|
||||||
|
NETWORK_TOPOLOGY = 'routed'
|
||||||
#END_MURANO_DASHBOARD
|
#END_MURANO_DASHBOARD
|
||||||
EOF
|
EOF
|
||||||
if [ $? -ne 0 ];then
|
if [ $? -ne 0 ];then
|
||||||
|
|
2
setup.sh
2
setup.sh
|
@ -99,6 +99,8 @@ LOGGING['loggers']['muranoclient'] = {'handlers': ['murano-file'], 'level': 'ERR
|
||||||
#MURANO_METADATA_URL = "http://localhost:8084/v1"
|
#MURANO_METADATA_URL = "http://localhost:8084/v1"
|
||||||
#if murano-api set up with ssl uncomment next strings
|
#if murano-api set up with ssl uncomment next strings
|
||||||
#MURANO_API_INSECURE = True
|
#MURANO_API_INSECURE = True
|
||||||
|
ADVANCED_NETWORKING_CONFIG = {'max_environments': 100, 'max_hosts': 250, 'env_ip_template': '10.0.0.0'}
|
||||||
|
NETWORK_TOPOLOGY = 'routed'
|
||||||
#END_MURANO_DASHBOARD
|
#END_MURANO_DASHBOARD
|
||||||
EOF
|
EOF
|
||||||
if [ $? -ne 0 ];then
|
if [ $? -ne 0 ];then
|
||||||
|
|
Loading…
Reference in New Issue