octavia-dashboard/octavia_dashboard/dashboards/project/loadbalancersv2/workflows/create_lb.py

461 lines
18 KiB
Python

# Copyright 2015, eBay 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.
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import workflows
from openstack_dashboard.api import nova
from octavia_dashboard import api
__create_new__ = "Create New"
class SetLBDetailsAction(workflows.Action):
address = forms.ChoiceField(label=_("IP"),
help_text=_("Select from existing VIP IPs"))
name = forms.CharField(max_length=80, label=_("Name"),
required=True)
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 2}),
label=_(
"Load Balancer Description"),
required=False,
help_text=_("Provide Load Balancer "
"Description."))
all_vips = None
is_update = False
LOAD_BALANCING_CHOICES = (
("RoundRobin", _("Round Robin")),
("LeastConnection", _("Least Connection")),
("LeastSessions", _("Least Sessions"))
)
lb_method = forms.ChoiceField(label=_("Load Balancing Method"),
choices=LOAD_BALANCING_CHOICES)
PROTOCOL_CHOICES = (
("HTTP", _("HTTP")),
("HTTPS", _("HTTPS")),
("TCP", _("TCP")),
("SSL", _("SSL")),
)
protocol_type = forms.ChoiceField(
label=_("LB Protocol"), choices=PROTOCOL_CHOICES)
port = forms.IntegerField(label=_("LB Port"),
required=False,
min_value=1,
max_value=65535,
help_text=_("LB Port on which "
"LB is listening."))
instance_port = forms.IntegerField(label=_("Instance Port"),
required=False,
min_value=1,
max_value=65535,
help_text=_("Instance Port on which "
"service is running."))
def __init__(self, request, *args, **kwargs):
super(SetLBDetailsAction, self).__init__(request, *args, **kwargs)
self.all_vips = []
try:
# todo - this should be obtained in view via an initial method
self.all_vips = api.lbaasv2.list_loadbalancers(request)
except Exception:
pass
if len(self.fields['address'].choices) == 0:
del self.fields['address']
class Meta(object):
name = _("LB Details")
help_text_template = ("project/loadbalancersv2/_launch_lb_help.html")
def clean(self):
cleaned_data = super(SetLBDetailsAction, self).clean()
lb_method = cleaned_data['lb_method']
if not (lb_method == 'RoundRobin'
or lb_method == 'LeastConnection'
or lb_method == 'LeastSessions'):
raise forms.ValidationError(_("Please select an option for "
"the load balancing method."))
if not self.is_update:
all_vips = self.all_vips
ipPortCombo = []
for vip in all_vips:
vip = vip.readable()
ipPortCombo.append('%s:%s' % (vip.address, vip.port))
data = self.data
if 'address' in data \
and data['address'] != 'new' \
and data['address'] != '':
address = data['address']
selected_lb_port = data['port']
selected_ip_port_combo = '%s:%s' % (address.split(':')[0],
selected_lb_port)
if selected_ip_port_combo in ipPortCombo:
raise forms.ValidationError(_('Requested IP and port '
'combination already '
'exists %s ') %
selected_ip_port_combo)
instance_port = cleaned_data.get('instance_port', None)
if not instance_port:
raise forms.ValidationError(
_('Please provide instance port'))
return cleaned_data
def populate_address_choices(self, request, context):
if self.is_update:
return []
try:
vips = api.lbaasv2.list_loadbalancers(request)
if len(vips) == 0:
return []
distict_ips = set()
for vip in vips:
vip = vip.readable()
distict_ips.add(vip.address)
existing = []
for vip in vips:
vip = vip.readable()
if vip.address in distict_ips:
item = ("%s:%s:%s" %
(vip.address, vip.name, 443),
"%s" % vip.address)
existing.append(item)
distict_ips.remove(vip.address)
vip_list = []
if len(existing) > 0:
vip_list.append(('new', __create_new__))
vip_list.append(('Select Existing', existing))
return vip_list
except Exception:
exceptions.handle(request,
_('Unable to retrieve vips.'))
return []
def get_help_text(self):
extra = {}
return super(SetLBDetailsAction, self).get_help_text(extra)
class SetLBDetails(workflows.Step):
action_class = SetLBDetailsAction
contributes = ("name", "description", "lb_method", "protocol_type", "port",
"source_id", "instance_port", "address", "monitor")
def contribute(self, data, context):
context = super(SetLBDetails, self).contribute(data, context)
return context
template_name = "project/loadbalancersv2/launch_lb.html"
class UploadSSLAction(workflows.Action):
update_cert = forms.BooleanField(label='Update SSL Certificate',
required=False,
widget=forms.HiddenInput())
cert_name = forms.CharField(max_length=80,
label=_("Certificate Name"),
required=False)
cert = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}),
label=_("Certificate"),
required=False,
help_text=_("Certificate"))
private_key = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}),
label=_("Private Key"),
required=False,
help_text=_("Private Key"))
chain_cert = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}),
label=_("Certificate Chain (Optional)"),
required=False,
help_text=_("Intermediate Chain"
" Certificates"))
def clean(self):
cleaned_data = super(UploadSSLAction, self).clean()
data = self.data
protocol = data.get('source_type')
if protocol == 'HTTPS':
use_common_cert = data.get('use_common_cert')
if not use_common_cert:
# check to see if ssl cert is provided
cert_name = data.get('cert_name')
cert = data.get('cert')
private_key = data.get('private_key')
if (not cert_name) \
or (not cert) \
or (not private_key):
raise forms.ValidationError(
_('Please provide all certificate parameters.'))
return cleaned_data
class Meta(object):
name = _("SSL Certificate")
help_text_template = ("project/loadbalancersv2/_ssl_cert_help.html")
class UploadSSLStep(workflows.Step):
action_class = UploadSSLAction
contributes = ("cert_name", "cert",
"private_key", "chain_cert", 'use_common_cert')
template_name = "project/loadbalancersv2/ssl_cert.html"
def contribute(self, data, context):
post = self.workflow.request.POST
context['cert_name'] = post['cert_name'] if 'cert_name' in post else ''
context['cert'] = post['cert'] if 'cert' in post else ''
context['private_key'] = post[
'private_key'] if 'private_key' in post else ''
context['chain_cert'] = post[
'chain_cert'] if 'chain_cert' in post else ''
context['use_common_cert'] = post[
'use_common_cert'] if 'use_common_cert' in post else ''
return context
class SelectInstancesAction(workflows.MembershipAction):
instance_details = {}
def __init__(self, request, *args, **kwargs):
super(SelectInstancesAction, self).__init__(request, *args, **kwargs)
err_msg = _('Unable to retrieve members list. '
'Please try again later.')
default_role_field_name = self.get_default_role_field_name()
self.fields[default_role_field_name] = forms.CharField(required=False,
label='')
self.fields[default_role_field_name].initial = 'member'
role_member_field_name = self.get_member_field_name('member')
self.fields[role_member_field_name] = forms.MultipleChoiceField(
required=False, label='')
# Get list of available instances
all_instances = []
try:
all_instances, has_more_data = nova.server_list(request)
except Exception:
exceptions.handle(request, err_msg)
available_instances = []
for instance in all_instances:
# skip shutoff instances
# if instance.status == 'SHUTOFF':
# continue
instance_ip = self.get_ip(instance)
# skip instances which has no network
if not instance_ip:
continue
key = instance_ip
value = instance.name + ' (' + self.get_ip(instance) + ')'
available_instances.append((key, value))
self.instance_details[instance_ip] = (instance.name, instance.id)
self.fields[self.get_member_field_name('member')].\
choices = available_instances
def get_ip(self, instance):
ipaddress = None
for networks in instance.addresses.itervalues():
for ip in networks:
# only one IP present
ipaddress = ip
break
if ipaddress is not None:
addr = ipaddress["addr"]
else:
addr = None # '10.10.10.10'
return addr
def clean(self):
cleaned_data = super(SelectInstancesAction, self).clean()
members = cleaned_data.get(self.get_member_field_name('member'), None)
if not members:
raise forms.ValidationError(
_('Please select at least one member'))
return cleaned_data
class Meta(object):
name = _("Instances")
slug = "select_instances"
class SelectInstancesStep(workflows.UpdateMembersStep):
action_class = SelectInstancesAction
help_text = _("Please select a list of instances that should handle"
" traffic for this target load balancer. All instances "
"must reside in the same Project as the target load "
"balancer.")
available_list_title = _("All Instances")
members_list_title = _("Selected Instances")
no_available_text = _("No instances found.")
no_members_text = _("No members enabled.")
show_roles = False
contributes = (
"wanted_members", "instances_details", "monitor", "instance_port")
template_name = "horizon/common/_workflow_step_update_members.html"
def contribute(self, data, context):
request = self.workflow.request
if data:
context["wanted_members"] = request.POST.getlist(
self.get_member_field_name('member'))
context["instances_details"] = self.action.instance_details
context["monitor"] = request.POST.get("monitor")
context["instance_port"] = request.POST.get("instance_port")
return context
class SelectMonitorAction(workflows.Action):
MONITOR_CHOICES = (
("tcp", _("TCP")),
("ping", _("PING")),
("http", _("HTTP")),
)
monitor = forms.ChoiceField(label=_("Monitor"),
choices=MONITOR_CHOICES)
interval = forms.IntegerField(label=_("Health Check Interval"
" (in seconds)"),
required=False,
min_value=1,
max_value=600,
help_text=_("Health Check Interval"
" (in seconds)"))
timeout = forms.IntegerField(label=_("Retry count before markdown"),
required=False,
min_value=1,
max_value=100,
help_text=_("Number of times health check "
"should be attempted before "
"marking down a member"))
send = forms.CharField(widget=forms.Textarea(attrs={'rows': 1}),
label=_("Send String"),
required=False,
help_text=_("Send String"))
receive = forms.CharField(widget=forms.Textarea(attrs={'rows': 1}),
label=_("Receive String"),
required=False,
help_text=_("Receive String"))
class Meta(object):
name = _("Monitor")
help_text_template = ("project/loadbalancersv2/_monitor_help.html")
class SelectMonitorStep(workflows.Step):
action_class = SelectMonitorAction
contributes = ("monitor", "interval", "timeout", "send", "receive")
template_name = "project/loadbalancersv2/_monitor_create.html"
def contribute(self, data, context):
post = self.workflow.request.POST
context['interval'] = post['interval'] if 'interval' in post else ''
context['timeout'] = post['timeout'] if 'timeout' in post else ''
context['send'] = post['send'] if 'send' in post else ''
context['receive'] = post['receive'] if 'receive' in post else ''
return context
class LaunchLoadBalancer(workflows.Workflow):
slug = "launch_loadbalancer"
name = _("Launch Load Balancer")
finalize_button_name = _("Launch")
success_message = _('Launched %(count)s named "%(name)s".')
failure_message = _('Unable to launch %(count)s named "%(name)s".')
success_url = "horizon:project:loadbalancersv2:index"
default_steps = (SetLBDetails,
UploadSSLStep,
SelectMonitorStep,
SelectInstancesStep,
)
attrs = {'data-help-text': 'LB creation may take a few minutes'}
def format_status_message(self, message):
name = self.context.get('name', 'unknown loadbalancer')
count = self.context.get('count', 1)
if int(count) > 1:
return message % {"count": _("%s loadbalancers") % count,
"name": name}
else:
return message % {"count": _("loadbalancer"), "name": name}
def handle(self, request, context):
try:
protocol = context['source_type']
address = context['address']
if not address\
or address == "new":
address = ''
else:
tokens = address.split(':')
address = tokens[0]
api.lbaasv2.\
create_loadbalancer_full(request,
address=address,
name=context['name'],
description=context['description'],
lb_method=context['lb_method'],
monitor=context['monitor'],
protocol=protocol,
port=context[protocol],
instance_port=context['instance_port'], # noqa
wanted_members=context['wanted_members'], # noqa
instances_details=context['instances_details'], # noqa
cert_name=context['cert_name'],
cert=context['cert'],
private_key=context['private_key'],
chain_cert=context['chain_cert'],
use_common_cert=True if
context['use_common_cert'] == 'on'
else False,
interval=context['interval'],
timeout=context['timeout'],
send=context['send'],
receive=context['receive'],
)
return True
except Exception as e:
exceptions.handle(request, e.message, ignore=False)
return False