diff --git a/openstack_dashboard/api/lbaas.py b/openstack_dashboard/api/lbaas.py index 3070e4978b3..3c776b96a6d 100644 --- a/openstack_dashboard/api/lbaas.py +++ b/openstack_dashboard/api/lbaas.py @@ -115,6 +115,21 @@ def __init__(self, apiresource): super(PoolMonitor, self).__init__(apiresource) +class SSLPolicy(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron load balancer ssl policy""" + + def __init__(self, apiresource): + super(SSLPolicy, self).__init__(apiresource) + +class SSLCertificate(neutron.NeutronAPIDictWrapper): + """Wrapper for neutron load balancer ssl certificate""" + + def __init__(self, apiresource): + super(SSLCertificate, self).__init__(apiresource) + + + + def vip_create(request, **kwargs): """Create a vip for a specified pool. @@ -264,6 +279,104 @@ def pool_health_monitor_delete(request, mon_id): neutronclient(request).delete_health_monitor(mon_id) +def ssl_policy_create(request, **kwargs): + """Create a SSL Policy + + :param request: request context + :param name: name of ssl policy + :param description: description of ssl policy + :param front_end_enabled: front_end_enabled + :param front_end_cipher_suites: front_end_cipher_suites + :param back_end_enabled: back_end_enabled + :param back_end_cipher_suites: back_end_cipher_suites + """ + body = {'ssl_policy': {'name': kwargs['name'], + 'description': kwargs['description'], + 'front_end_enabled': kwargs['front_end_enabled'], + 'front_end_cipher_suites': kwargs['front_end_cipher_suites'], + 'front_end_protocols': kwargs['front_end_protocols'], + 'back_end_enabled': 'False', + 'back_end_cipher_suites': '', + 'back_end_protocols':'' + }} + + ssl = neutronclient(request).create_ssl_policy(body).get('ssl_policy') + + return SSLPolicy(ssl) + + +def ssl_policies_get(request, **kwargs): + sslpolicies = neutronclient(request).list_ssl_policies().get('ssl_policies') + return [SSLPolicy(m) for m in sslpolicies] + +def ssl_policy_get(request, sslpolicy_id): + sslpolicy = neutronclient(request).show_ssl_policy(sslpolicy_id).get('ssl_policy') + return SSLPolicy(sslpolicy) + +def ssl_policy_update(request, sslpolicy_id, **kwargs): + sslpolicy = neutronclient(request).update_ssl_policy(sslpolicy_id, kwargs) + return SSLPolicy(sslpolicy) + +def ssl_policy_delete(request, sslpolicy_id): + neutronclient(request).delete_ssl_policy(sslpolicy_id) + +def ssl_certificate_create(request, **kwargs): + """Create a SSL Certificate + + :param request: request context + :param name: name of ssl certificate + :param certificate: certificate + :param passphrase: passphrase + :param certificate_chain: certificate_chain + """ + body = {'ssl_certificate': {'name': kwargs['name'], + 'certificate': kwargs['certificate'], + 'passphrase': kwargs['passphrase'], + 'certificate_chain': kwargs['certificate_chain']}} + + sslcertificate = neutronclient(request).create_ssl_certificate(body).get('ssl_certificate') + + return SSLCertificate(sslcertificate) + +def ssl_certificates_get(request, **kwargs): + sslcertificates = neutronclient(request).list_ssl_certificates().get('ssl_certificates') + return [SSLCertificate(m) for m in sslcertificates] + +def ssl_certificate_get(request, sslcertificate_id): + sslcertificate = neutronclient(request).show_ssl_certificate(sslcertificate_id).get('ssl_certificate') + return SSLCertificate(sslcertificate) + +def ssl_certificate_update(request, sslcertificate_id, **kwargs): + sslpolicy = neutronclient(request).update_ssl_certificate(sslcertificate_id, kwargs) + return SSLCertificate(sslpolicy) + +def ssl_certificate_delete(request, sslcertificate_id): + neutronclient(request).delete_ssl_certificate(sslcertificate_id) + +def ssl_policy_associate(request, **kwargs): + + body = {'ssl_association': { + 'id': kwargs['vip_id'], + 'ssl_policy':{ + 'id':kwargs['ssl_policy_id']}, + 'ssl_certificates': [ + { 'id':kwargs['ssl_certificate_id'], + 'private_key':kwargs['private_key']} + ], + 'ssl_trusted_certificates':[], + }} + neutronclient(request).create_ssl_policy_association(kwargs['vip_id'], body) + +def ssl_policy_disassociate(request, **kwargs): + + neutronclient(request).delete_ssl_policy_association( + kwargs['vip_id'], + kwargs['ssl_policy_id']) + + + + + def member_create(request, **kwargs): """Create a load balance member diff --git a/openstack_dashboard/dashboards/project/loadbalancers/forms.py b/openstack_dashboard/dashboards/project/loadbalancers/forms.py index f3e517fa1ac..27ad814ec9b 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/forms.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/forms.py @@ -257,3 +257,117 @@ def handle(self, request, context): LOG.info(msg) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect) + +FRONT_END_PROTOCOL_CHOICES = ( + ('SSLv2','SSLv2'), + ('SSLv3','SSLv3'), + ('TLSv1','TLSv1'), + ) + +class UpdateSSLpolicy(forms.SelfHandlingForm): + sslpolicy_id = forms.CharField(label=_("ID"), + widget=forms.TextInput( + attrs={'readonly': 'readonly'})) + + name = forms.CharField(max_length=80, label=_("Name")) + description = forms.CharField(required=False, + max_length=80, label=_("Description")) + + front_end_enabled = forms.BooleanField(label=_("Front End Enabled"), + initial=True, required=False) + + front_end_cipher_suites = forms.ChoiceField( + label=_("Front End Cipher Suite"), + choices=[('ALL', _('ALL')), + ('HIGH', _('HIGH')), + ('MEDIUM', _('MEDIUM')), + ('LOW', _('LOW'))]) + + front_end_protocols = forms.TypedMultipleChoiceField(required=False, + label=_("Front End Protocols"), + empty_value='', + widget=forms.CheckboxSelectMultiple, + choices=FRONT_END_PROTOCOL_CHOICES) + """ + back_end_enabled = forms.BooleanField(label=_("Back End Enabled"), + initial=True, required=False) + + back_end_cipher_suites = forms.ChoiceField( + label=_("Back End Cipher Suite"), + choices=[('ALL', _('ALL')), + ('HIGH', _('HIGH')), + ('MEDIUM', _('MEDIUM')), + ('LOW', _('LOW'))]) + """ + failure_url = 'horizon:project:loadbalancers:index' + + def __init__(self, request, *args, **kwargs): + super(UpdateSSLpolicy, self).__init__(request, *args, **kwargs) + + def handle(self, request, context): + try: + data = {'ssl_policy': { + 'name': context['name'], + 'description': context['description'], + 'front_end_enabled': context['front_end_enabled'], + 'front_end_protocols': ','.join(context['front_end_protocols']), + 'front_end_cipher_suites': context['front_end_cipher_suites']}} + + #'back_end_enabled': context['back_end_enabled'], + #'back_end_cipher_suites': context['back_end_cipher_suites']}} + + sslpolicy = api.lbaas.ssl_policy_update(request, + context['sslpolicy_id'], **data) + msg = _('SSL policy %s was successfully updated.')\ + % context['sslpolicy_id'] + LOG.debug(msg) + messages.success(request, msg) + return sslpolicy + except Exception: + msg = _('Failed to update SSL policy %s')\ + % context['sslpolicy_id'] + LOG.info(msg) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) + + +class UpdateSSLcertificate(forms.SelfHandlingForm): + sslcertificate_id = forms.CharField(label=_("ID"), + widget=forms.TextInput( + attrs={'readonly': 'readonly'})) + + name = forms.CharField(max_length=80, label=_("Name")) + passphrase = forms.CharField(required=False, + label=_("Passphrase")) + certificate_chain = forms.CharField(required=False, + label=_("Certificate Chain")) + certificate = forms.CharField(required=False, + widget=forms.Textarea, + label=_("Certificate")) + + failure_url = 'horizon:project:loadbalancers:index' + + def __init__(self, request, *args, **kwargs): + super(UpdateSSLcertificate, self).__init__(request, *args, **kwargs) + + def handle(self, request, context): + try: + data = {'ssl_certificate': { + 'name': context['name'], + 'passphrase': context['passphrase'], + 'certificate_chain': context['certificate_chain'], + 'certificate': context['certificate']}} + + sslcertificate = api.lbaas.ssl_certificate_update(request, + context['sslcertificate_id'], **data) + msg = _('SSL certificate %s was successfully updated.')\ + % context['sslcertificate_id'] + LOG.debug(msg) + messages.success(request, msg) + return sslcertificate + except Exception: + msg = _('Failed to update SSL certificate %s')\ + % context['sslcertificate_id'] + LOG.info(msg) + redirect = reverse(self.failure_url) + exceptions.handle(request, msg, redirect=redirect) diff --git a/openstack_dashboard/dashboards/project/loadbalancers/tables.py b/openstack_dashboard/dashboards/project/loadbalancers/tables.py index 734498d97c4..37363376bae 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/tables.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/tables.py @@ -62,6 +62,17 @@ class AddMonitorLink(tables.LinkAction): url = "horizon:project:loadbalancers:addmonitor" classes = ("ajax-modal", "btn-create",) +class AddSSLpolicyLink(tables.LinkAction): + name = "addsslpolicy" + verbose_name = _("Add SSL Policy") + url = "horizon:project:loadbalancers:addsslpolicy" + classes = ("ajax-modal", "btn-create",) + +class AddSSLcertificateLink(tables.LinkAction): + name = "addcertificate" + verbose_name = _("Add SSL Certificate") + url = "horizon:project:loadbalancers:addcertificate" + classes = ("ajax-modal", "btn-create",) class DeleteVipLink(tables.DeleteAction): name = "deletevip" @@ -92,6 +103,22 @@ class DeleteMonitorLink(tables.DeleteAction): data_type_plural = _("Monitors") +class DeleteSSLpolicyLink(tables.DeleteAction): + name = "deletesslpolicy" + action_present = _("Delete") + action_past = _("Scheduled deletion of") + data_type_singular = _("SSL Policy") + data_type_plural = _("SSL Policies") + + +class DeleteSSLcertificateLink(tables.DeleteAction): + name = "deletesslcertificate" + action_present = _("Delete") + action_past = _("Scheduled deletion of") + data_type_singular = _("SSL Certificate") + data_type_plural = _("SSL Certificates") + + class DeleteMemberLink(tables.DeleteAction): name = "deletemember" action_present = _("Delete") @@ -144,6 +171,25 @@ def get_link_url(self, monitor): base_url = reverse("horizon:project:loadbalancers:updatemonitor", kwargs={'monitor_id': monitor.id}) return base_url + + +class UpdateSSLpolicyLink(tables.LinkAction): + name = "updatesslpolicy" + verbose_name = _("Edit SSL Policy") + + def get_link_url(self, sslpolicy): + base_url = reverse("horizon:project:loadbalancers:updatesslpolicy", + kwargs={'sslpolicy_id': sslpolicy.id}) + return base_url + +class UpdateSSLcertificateLink(tables.LinkAction): + name = "updatesslcertificate" + verbose_name = _("Edit SSL Certificate") + + def get_link_url(self, sslcertificate): + base_url = reverse("horizon:project:loadbalancers:updatesslcertificate", + kwargs={'sslcertificate_id': sslcertificate.id}) + return base_url def get_vip_link(pool): @@ -186,6 +232,54 @@ def allowed(self, request, datum=None): return True +class AssociateSSLPolicyLink(tables.LinkAction): + name = "associatesslpolicy" + verbose_name = _("Associate SSL Policy") + classes = ("ajax-modal", "btn-create",) + + def get_link_url(self, pool): + base_url = reverse("horizon:project:loadbalancers:associatesslpolicy", + kwargs={'vip_id': pool.vip_id}) + return base_url + + def allowed(self, request, datum=None): + if datum and not datum.vip_id: + return False + else: + try: + vip = api.lbaas.vip_get(request, datum.vip_id) + if vip['ssl_policy_id'] != None or vip['protocol'] != 'HTTPS': + return False + except Exception: + exceptions.handle(request, _('Failed to retrieve VIP')) + + return True + + +class DisassociateSSLPolicyLink(tables.LinkAction): + name = "disassociatesslpolicy" + verbose_name = _("Disassociate SSL Policy") + classes = ("ajax-modal", "btn-create",) + + def get_link_url(self, pool): + base_url = reverse("horizon:project:loadbalancers:disassociatesslpolicy", + kwargs={'vip_id': pool.vip_id}) + return base_url + + def allowed(self, request, datum=None): + if datum and datum.vip_id: + try: + vip = api.lbaas.vip_get(request, datum.vip_id) + if vip['ssl_policy_id'] != None: + return True + + except Exception: + exceptions.handle(request, _('Failed to retrieve VIP')) + + return False + + + class PoolsTable(tables.DataTable): name = tables.Column("name", verbose_name=_("Name"), @@ -204,7 +298,8 @@ class Meta: table_actions = (AddPoolLink, DeletePoolLink) row_actions = (UpdatePoolLink, AddVipLink, UpdateVipLink, DeleteVipLink, AddPMAssociationLink, - DeletePMAssociationLink, DeletePoolLink) + DeletePMAssociationLink, DeletePoolLink, + AssociateSSLPolicyLink, DisassociateSSLPolicyLink) def get_pool_link(member): @@ -245,3 +340,37 @@ class Meta: verbose_name = _("Monitors") table_actions = (AddMonitorLink, DeleteMonitorLink) row_actions = (UpdateMonitorLink, DeleteMonitorLink) + +class SSLpoliciesTable(tables.DataTable): + name = tables.Column("name", + verbose_name=_("Name"), + link="horizon:project:loadbalancers:sslpolicydetails") + description = tables.Column('description', verbose_name=_("Description")) + front_end_enabled = tables.Column('front_end_enabled', verbose_name=_("Front End Enabled")) + front_end_cipher_suites = tables.Column('front_end_cipher_suites', verbose_name=_("Front End Cipher Suites")) + front_end_protocols = tables.Column('front_end_protocols', verbose_name=_("Front End Protocols")) + #back_end_enabled = tables.Column('back_end_enabled', verbose_name=_("Back End Enabled")) + #back_end_cipher_suites = tables.Column('back_end_cipher_suites', verbose_name=_("Back End Cipher Suites")) + + + class Meta: + name = "sslpoliciestable" + verbose_name = _("SSL Policies") + table_actions = (AddSSLpolicyLink, DeleteSSLpolicyLink) + row_actions = (UpdateSSLpolicyLink, DeleteSSLpolicyLink) + + +class SSLcertificatesTable(tables.DataTable): + name = tables.Column("name", + verbose_name=_("Name"), + link="horizon:project:loadbalancers:sslcertificatedetails") + #certificate = tables.Column('certificate', verbose_name=_("Certificate")) + passphrase = tables.Column('passphrase', verbose_name=_("Passphrase")) + certificate_chain = tables.Column('certificate_chain', verbose_name=_("Certificate Chain")) + + + class Meta: + name = "sslcertificatestable" + verbose_name = _("SSL Certificates") + table_actions = (AddSSLcertificateLink, DeleteSSLcertificateLink) + row_actions = (UpdateSSLcertificateLink, DeleteSSLcertificateLink) diff --git a/openstack_dashboard/dashboards/project/loadbalancers/tabs.py b/openstack_dashboard/dashboards/project/loadbalancers/tabs.py index ca610dc2b73..003535e1fe2 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/tabs.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/tabs.py @@ -82,10 +82,43 @@ def get_monitorstable_data(self): _('Unable to retrieve monitor list.')) return monitors +class SSLpoliciesTab(tabs.TableTab): + table_classes = (tables.SSLpoliciesTable,) + name = _("SSL Policies") + slug = "sslpolicies" + template_name = "horizon/common/_detail_table.html" + + def get_sslpoliciestable_data(self): + try: + tenant_id = self.request.user.tenant_id + sslpolicies = api.lbaas.ssl_policies_get( + self.tab_group.request, tenant_id=tenant_id) + except Exception: + sslpolicies = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve SSL policy list.')) + return sslpolicies + +class SSLcertificateTab(tabs.TableTab): + table_classes = (tables.SSLcertificatesTable,) + name = _("SSL Certificates") + slug = "sslcertificates" + template_name = "horizon/common/_detail_table.html" + + def get_sslcertificatestable_data(self): + try: + tenant_id = self.request.user.tenant_id + sslcertificates = api.lbaas.ssl_certificates_get( + self.tab_group.request, tenant_id=tenant_id) + except Exception: + sslcertificates = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve SSL Certificate list.')) + return sslcertificates class LoadBalancerTabs(tabs.TabGroup): slug = "lbtabs" - tabs = (PoolsTab, MembersTab, MonitorsTab) + tabs = (PoolsTab, MembersTab, MonitorsTab, SSLpoliciesTab, SSLcertificateTab) sticky = True @@ -153,6 +186,36 @@ def get_context_data(self, request): return {'monitor': monitor} +class SSLpolicyDetailsTab(tabs.Tab): + name = _("SSL Policy Details") + slug = "sslpolicydetails" + template_name = "project/loadbalancers/_sslpolicy_details.html" + + def get_context_data(self, request): + sslpolicyid = self.tab_group.kwargs['sslpolicy_id'] + try: + sslpolicy = api.lbaas.ssl_policy_get(request, sslpolicyid) + except Exception: + sslpolicy = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve SSL policy details.')) + return {'sslpolicy': sslpolicy} + +class SSLcertificateDetailsTab(tabs.Tab): + name = _("SSL Certificate Details") + slug = "sslcertificatedetails" + template_name = "project/loadbalancers/_sslcertificate_details.html" + + def get_context_data(self, request): + sslcertificateid = self.tab_group.kwargs['sslcertificate_id'] + try: + sslcertificate = api.lbaas.ssl_certificate_get(request, sslcertificateid) + except Exception: + sslcertificate = [] + exceptions.handle(self.tab_group.request, + _('Unable to retrieve SSL certificate details.')) + return {'sslcertificate': sslcertificate} + class PoolDetailsTabs(tabs.TabGroup): slug = "pooltabs" tabs = (PoolDetailsTab,) @@ -171,3 +234,12 @@ class MemberDetailsTabs(tabs.TabGroup): class MonitorDetailsTabs(tabs.TabGroup): slug = "monitortabs" tabs = (MonitorDetailsTab,) + + +class SSLPolicyDetailsTabs(tabs.TabGroup): + slug = "sslpolicytabs" + tabs = (SSLpolicyDetailsTab,) + +class SSLcertificateDetailsTabs(tabs.TabGroup): + slug = "sslcertificatetabs" + tabs = (SSLcertificateDetailsTab,) \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslcertificate_details.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslcertificate_details.html new file mode 100644 index 00000000000..5a53020fcc3 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslcertificate_details.html @@ -0,0 +1,22 @@ +{% load i18n sizeformat parse_date %} + +
+
+
+
{% trans "ID" %}
+
{{ sslcertificate.id }}
+ +
{% trans "Name" %}
+
{{ sslcertificate.name }}
+ +
{% trans "Passphrase" %}
+
{{ sslcertificate.passphrase }}
+ +
{% trans "Certificate Chain" %}
+
{{ sslcertificate.certificate_chain }}
+ +
{% trans "Certificate" %}
+
{{ sslcertificate.certificate }}
+ +
+
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicies_tab.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicies_tab.html new file mode 100644 index 00000000000..c7677aa435e --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicies_tab.html @@ -0,0 +1,5 @@ +{% block main %} + + {{ table.render }} + +{% endblock %} \ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicy_details.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicy_details.html new file mode 100644 index 00000000000..a912ffa0452 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_sslpolicy_details.html @@ -0,0 +1,37 @@ +{% load i18n sizeformat parse_date %} + +
+
+
+
{% trans "ID" %}
+
{{ sslpolicy.id }}
+ +
{% trans "Name" %}
+
{{ sslpolicy.name }}
+ +
{% trans "Description" %}
+
{{ sslpolicy.description }}
+ +
{% trans "Project ID" %}
+
{{ sslpolicy.tenant_id }}
+ +
{% trans "Front End Enabled" %}
+
{{ sslpolicy.front_end_enabled }}
+ +
{% trans "Front End Cipher Suites" %}
+
{{ sslpolicy.front_end_cipher_suites }}
+ +
{% trans "Front End Protocols" %}
+
{{ sslpolicy.front_end_protocols }}
+ +
+
\ No newline at end of file diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslcertificate.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslcertificate.html new file mode 100644 index 00000000000..9896c702f11 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslcertificate.html @@ -0,0 +1,25 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}update_sslcertificate_form{% endblock %} +{% block form_action %}{% url 'horizon:project:loadbalancers:updatesslcertificate' sslcertificate_id %}{% endblock %} + +{% block modal-header %}{% trans "Edit SSL Certificate" %}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "You may update SSL certificate for current tenant here: edit name, certificate, passphrase, certificate chain." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslpolicy.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslpolicy.html new file mode 100644 index 00000000000..4c066487746 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/_updatesslpolicy.html @@ -0,0 +1,25 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}update_sslpolicy_form{% endblock %} +{% block form_action %}{% url 'horizon:project:loadbalancers:updatesslpolicy' sslpolicy_id %}{% endblock %} + +{% block modal-header %}{% trans "Edit SSL Policy" %}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description:" %}

+

{% trans "You may update SSL policy for current tenant here: edit name, description." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/disassociateSsl.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/disassociateSsl.html new file mode 100644 index 00000000000..908d0da2822 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/disassociateSsl.html @@ -0,0 +1,28 @@ +{% load i18n %} +{% with workflow.get_entry_point as entry_point %} + +{% endwith %} +{% block modal-js %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslcertificate.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslcertificate.html new file mode 100644 index 00000000000..3f19e9887f2 --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslcertificate.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit SSL Certificate" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Edit SSL Certificate") %} +{% endblock page_header %} + +{% block main %} + {% include 'project/loadbalancers/_updatesslcertificate.html' %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslpolicy.html b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslpolicy.html new file mode 100644 index 00000000000..392844ea70f --- /dev/null +++ b/openstack_dashboard/dashboards/project/loadbalancers/templates/loadbalancers/updatesslpolicy.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit SSL Policy" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Edit SSL Policy") %} +{% endblock page_header %} + +{% block main %} + {% include 'project/loadbalancers/_updatesslpolicy.html' %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/loadbalancers/tests.py b/openstack_dashboard/dashboards/project/loadbalancers/tests.py index 6167daee711..881fa73dc44 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/tests.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/tests.py @@ -54,8 +54,10 @@ def set_up_expect(self): IsA(http.HttpRequest), tenant_id=self.tenant.id) \ .AndReturn(self.pools.list()) - api.lbaas.vip_get(IsA(http.HttpRequest), vip1.id).AndReturn(vip1) - api.lbaas.vip_get(IsA(http.HttpRequest), vip2.id).AndReturn(vip2) + api.lbaas.vip_get(IsA(http.HttpRequest), vip1.id).MultipleTimes() \ + .AndReturn(vip1) + api.lbaas.vip_get(IsA(http.HttpRequest), vip2.id).MultipleTimes() \ + .AndReturn(vip2) # retrieves members api.lbaas.members_get( diff --git a/openstack_dashboard/dashboards/project/loadbalancers/urls.py b/openstack_dashboard/dashboards/project/loadbalancers/urls.py index 6bfd04c0e96..39a8fe540e5 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/urls.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/urls.py @@ -47,4 +47,18 @@ url(r'^member/(?P[^/]+)/$', views.MemberDetailsView.as_view(), name='memberdetails'), url(r'^monitor/(?P[^/]+)/$', - views.MonitorDetailsView.as_view(), name='monitordetails')) + views.MonitorDetailsView.as_view(), name='monitordetails'), + url(r'^addsslpolicy$', views.AddSSLpolicyView.as_view(), name='addsslpolicy'), + url(r'^addcertificate$', views.AddSSLcertificateView.as_view(), name='addcertificate'), + url(r'^sslpolicy/(?P[^/]+)/$', + views.SSLpolicyDetailsView.as_view(), name='sslpolicydetails'), + url(r'^updatesslpolicy/(?P[^/]+)/$', + views.UpdateSSLpolicyView.as_view(), name='updatesslpolicy'), + url(r'^sslcertificatedetails/(?P[^/]+)/$', + views.SSLcertificateDetailsView.as_view(), name='sslcertificatedetails'), + url(r'^updatesslcertificate/(?P[^/]+)/$', + views.UpdateSSLcertificateView.as_view(), name='updatesslcertificate'), + url(r'^associatesslpolicy/(?P[^/]+)/$', + views.AssociateSSLPolicyView.as_view(), name='associatesslpolicy'), + url(r'^disassociatesslpolicy/(?P[^/]+)/$', + views.DisassociateSSLPolicyView.as_view(), name='disassociatesslpolicy')) diff --git a/openstack_dashboard/dashboards/project/loadbalancers/views.py b/openstack_dashboard/dashboards/project/loadbalancers/views.py index d4ef769cea3..1d0ae0cdabb 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/views.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/views.py @@ -83,6 +83,22 @@ def post(self, request, *args, **kwargs): except Exception as e: exceptions.handle(request, _('Unable to delete VIP. %s') % e) + if m == 'sslpolicy': + for obj_id in obj_ids: + try: + api.lbaas.ssl_policy_delete(request, obj_id) + messages.success(request, _('Deleted SSL policy %s') % obj_id) + except Exception as e: + exceptions.handle(request, + _('Unable to delete SSL policy. %s') % e) + if m == 'sslcertificate': + for obj_id in obj_ids: + try: + api.lbaas.ssl_certificate_delete(request, obj_id) + messages.success(request, _('Deleted SSL certificate %s') % obj_id) + except Exception as e: + exceptions.handle(request, + _('Unable to delete SSL certificate. %s') % e) return self.get(request, *args, **kwargs) @@ -130,6 +146,19 @@ def get_initial(self): initial = super(AddMonitorView, self).get_initial() return initial +class AddSSLpolicyView(workflows.WorkflowView): + workflow_class = project_workflows.AddSSLpolicy + + def get_initial(self): + initial = super(AddSSLpolicyView, self).get_initial() + return initial + +class AddSSLcertificateView(workflows.WorkflowView): + workflow_class = project_workflows.AddSSLcertificate + + def get_initial(self): + initial = super(AddSSLcertificateView, self).get_initial() + return initial class PoolDetailsView(tabs.TabView): tab_group_class = (project_tabs.PoolDetailsTabs) @@ -149,6 +178,15 @@ class MemberDetailsView(tabs.TabView): class MonitorDetailsView(tabs.TabView): tab_group_class = (project_tabs.MonitorDetailsTabs) template_name = 'project/loadbalancers/details_tabs.html' + + +class SSLpolicyDetailsView(tabs.TabView): + tab_group_class = (project_tabs.SSLPolicyDetailsTabs) + template_name = 'project/loadbalancers/details_tabs.html' + +class SSLcertificateDetailsView(tabs.TabView): + tab_group_class = (project_tabs.SSLcertificateDetailsTabs) + template_name = 'project/loadbalancers/details_tabs.html' class UpdatePoolView(forms.ModalFormView): @@ -289,6 +327,115 @@ def get_initial(self): 'admin_state_up': monitor['admin_state_up']} +class UpdateSSLpolicyView(forms.ModalFormView): + form_class = project_forms.UpdateSSLpolicy + template_name = "project/loadbalancers/updatesslpolicy.html" + context_object_name = 'sslpolicy' + success_url = reverse_lazy("horizon:project:loadbalancers:index") + + def get_context_data(self, **kwargs): + context = super(UpdateSSLpolicyView, self).get_context_data(**kwargs) + context["sslpolicy_id"] = self.kwargs['sslpolicy_id'] + return context + + def _get_object(self, *args, **kwargs): + if not hasattr(self, "_object"): + sslpolicy_id = self.kwargs['sslpolicy_id'] + try: + self._object = api.lbaas.ssl_policy_get( + self.request, sslpolicy_id) + except Exception as e: + redirect = self.success_url + msg = _('Unable to retrieve SSL policy details. %s') % e + exceptions.handle(self.request, msg, redirect=redirect) + return self._object + + def get_initial(self): + sslpolicy = self._get_object() + return {'sslpolicy_id': sslpolicy['id'], + 'name': sslpolicy['name'], + 'description': sslpolicy['description'], + 'front_end_enabled': sslpolicy['front_end_enabled'], + 'front_end_protocols': sslpolicy['front_end_protocols'].split(','), + 'front_end_cipher_suites': sslpolicy['front_end_cipher_suites']} + #'back_end_enabled': sslpolicy['back_end_enabled'], + #'back_end_cipher_suites': sslpolicy['back_end_cipher_suites']} + + +class UpdateSSLcertificateView(forms.ModalFormView): + form_class = project_forms.UpdateSSLcertificate + template_name = "project/loadbalancers/updatesslcertificate.html" + context_object_name = 'sslcertificate' + success_url = reverse_lazy("horizon:project:loadbalancers:index") + + def get_context_data(self, **kwargs): + context = super(UpdateSSLcertificateView, self).get_context_data(**kwargs) + context["sslcertificate_id"] = self.kwargs['sslcertificate_id'] + return context + + def _get_object(self, *args, **kwargs): + if not hasattr(self, "_object"): + sslcertificate_id = self.kwargs['sslcertificate_id'] + try: + self._object = api.lbaas.ssl_certificate_get( + self.request, sslcertificate_id) + except Exception as e: + redirect = self.success_url + msg = _('Unable to retrieve SSL certificate details. %s') % e + exceptions.handle(self.request, msg, redirect=redirect) + return self._object + + def get_initial(self): + sslcertificate = self._get_object() + return {'sslcertificate_id': sslcertificate['id'], + 'name': sslcertificate['name'], + 'passphrase': sslcertificate['passphrase'], + 'certificate_chain': sslcertificate['certificate_chain'], + 'certificate': sslcertificate['certificate']} + +class AssociateSSLPolicyView(workflows.WorkflowView): + workflow_class = project_workflows.AssociateSSLPolicy + + def get_context_data(self, **kwargs): + context = super(AssociateSSLPolicyView, self).get_context_data(**kwargs) + context['vip_id'] = self.kwargs['vip_id'] + return context + + def get_initial(self): + initial = super(AssociateSSLPolicyView, self).get_initial() + initial['vip_id'] = self.kwargs['vip_id'] + return initial + + +class DisassociateSSLPolicyView(workflows.WorkflowView): + workflow_class = project_workflows.DisassociateSSLPolicy + ajax_template_name = "project/loadbalancers/disassociateSsl.html" + + def get_context_data(self, **kwargs): + context = super(DisassociateSSLPolicyView, self).get_context_data(**kwargs) + context['vip_id'] = self.kwargs['vip_id'] + return context + + def _get_object(self, *args, **kwargs): + if not hasattr(self, "_object"): + vip_id = self.kwargs['vip_id'] + try: + self._object = api.lbaas.vip_get(self.request, vip_id) + except Exception as e: + redirect = self.success_url + msg = _('Unable to retrieve VIP details. %s') % e + exceptions.handle(self.request, msg, redirect=redirect) + return self._object + + def get_initial(self): + vip = self._get_object() + return {'vip_id':vip['id'], + 'ssl_policy_id': vip['ssl_policy_id'] } + + + + + class AddPMAssociationView(workflows.WorkflowView): workflow_class = project_workflows.AddPMAssociation diff --git a/openstack_dashboard/dashboards/project/loadbalancers/workflows.py b/openstack_dashboard/dashboards/project/loadbalancers/workflows.py index b5d26d076a0..4720dca4160 100644 --- a/openstack_dashboard/dashboards/project/loadbalancers/workflows.py +++ b/openstack_dashboard/dashboards/project/loadbalancers/workflows.py @@ -493,6 +493,202 @@ class Meta: "HTTP codes upon success.") +FRONT_END_PROTOCOL_CHOICES = ( + ('SSLv2','SSLv2'), + ('SSLv3','SSLv3'), + ('TLSv1','TLSv1'), + ) + +class AddSSLpolicyAction(workflows.Action): + name = forms.CharField(max_length=80, label=_("Name")) + description = forms.CharField( + initial="", required=False, + max_length=80, label=_("Description")) + + front_end_enabled = forms.BooleanField(label=_("Front End Enabled"), + initial=True, required=False) + + front_end_cipher_suites = forms.ChoiceField( + label=_("Front End Cipher Suite"), + choices=[('ALL', _('ALL')), + ('HIGH', _('HIGH')), + ('MEDIUM', _('MEDIUM')), + ('LOW', _('LOW'))]) + + front_end_protocols = forms.TypedMultipleChoiceField(required=False, + label=_("Front End Protocols"), + empty_value='', + widget=forms.CheckboxSelectMultiple, + choices=FRONT_END_PROTOCOL_CHOICES) + + """ + back_end_enabled = forms.BooleanField(label=_("Back End Enabled"), + initial=True, required=False) + + back_end_cipher_suites = forms.ChoiceField( + label=_("Back End Cipher Suite"), + choices=[('ALL', _('ALL')), + ('HIGH', _('HIGH')), + ('MEDIUM', _('MEDIUM')), + ('LOW', _('LOW'))]) + """ + def __init__(self, request, *args, **kwargs): + super(AddSSLpolicyAction, self).__init__(request, *args, **kwargs) + + class Meta: + name = _("Add New SSL Policy") + permissions = ('openstack.services.network',) + help_text = _("Create a SSL Policy.\n\n") + +class AddSSLcertificateAction(workflows.Action): + name = forms.CharField(max_length=80, label=_("Name")) + passphrase = forms.CharField( + initial="", required=False, + label=_("Passphrase")) + certificate_chain = forms.CharField( + initial="", required=False, + label=_("Certificate Chain")) + certificate = forms.CharField( + initial="", required=False, + widget=forms.Textarea, + label=_("Certificate")) + + def __init__(self, request, *args, **kwargs): + super(AddSSLcertificateAction, self).__init__(request, *args, **kwargs) + + class Meta: + name = _("Add New SSL Certificate") + permissions = ('openstack.services.network',) + help_text = _("Create a SSL Certificate.\n\n") + + +class AssociateSSLPolicyAction(workflows.Action): + ssl_policy_id = forms.ChoiceField(label=_("SSL Policy"), required=True) + ssl_certificate_id = forms.ChoiceField(label=_("SSL Certificate"), required=True) + private_key = forms.CharField( + widget=forms.widgets.Textarea(), + initial="", required=True, + max_length=2048, label=_("Certificate Private Key")) + + def __init__(self, request, *args, **kwargs): + super(AssociateSSLPolicyAction, self).__init__(request, *args, **kwargs) + + ssl_policy_id_choices = [('', _("Select a SSL Policy"))] + try: + tenant_id = self.request.user.tenant_id + ssl_policies = api.lbaas.ssl_policies_get(request, tenant_id=tenant_id) + except Exception: + ssl_policies = [] + exceptions.handle(request, + _('Unable to retrieve SSL Policy list.')) + ssl_policies = sorted(ssl_policies, + key=lambda ssl_policy: ssl_policy.name) + for p in ssl_policies: + ssl_policy_id_choices.append((p.id, p.name)) + self.fields['ssl_policy_id'].choices = ssl_policy_id_choices + + ssl_certificate_id_choices = [('', _("Select SSL Certificate"))] + try: + tenant_id = self.request.user.tenant_id + ssl_certificates = api.lbaas.ssl_certificates_get(request, tenant_id=tenant_id) + except Exception: + ssl_certificates = [] + exceptions.handle(request, + _('Unable to retrieve SSL Certificate list.')) + ssl_policies = sorted(ssl_certificates, + key=lambda ssl_certificate: ssl_certificate.name) + for p in ssl_certificates: + ssl_certificate_id_choices.append((p.id, p.name)) + self.fields['ssl_certificate_id'].choices = ssl_certificate_id_choices + + def clean(self): + cleaned_data = super(AssociateSSLPolicyAction, self).clean() + return cleaned_data + + class Meta: + name = _("Associate SSL Policy with VIP") + permissions = ('openstack.services.network',) + help_text = _("Select an SSL Policy to associate with VIP. " + "Select SSL Certificate to be used. " + "Specify private key for chosen SSL certificate ") + + +class AssociateSSLPolicyStep(workflows.Step): + action_class = AssociateSSLPolicyAction + depends_on = ("vip_id",) + contributes = ("ssl_policy_id", "ssl_certificate_id", "private_key") + + def contribute(self, data, context): + context = super(AssociateSSLPolicyStep, self).contribute(data, context) + return context + + +class AssociateSSLPolicy(workflows.Workflow): + slug = "associatesslpolicy" + name = _("Associate SSL Policy") + finalize_button_name = _("Associate") + success_message = _('Associated.') + failure_message = _('Unable to associate.') + success_url = "horizon:project:loadbalancers:index" + default_steps = (AssociateSSLPolicyStep,) + + def format_status_message(self, message): + return message + + def handle(self, request, context): + try: + api.lbaas.ssl_policy_associate(request, **context) + return True + except Exception: + raise + return False + + +class DisassociateSSLPolicyAction(workflows.Action): + + def __init__(self, request, *args, **kwargs): + super(DisassociateSSLPolicyAction, self).__init__(request, *args, **kwargs) + + def clean(self): + cleaned_data = super(DisassociateSSLPolicyAction, self).clean() + return cleaned_data + + class Meta: + name = _("Disassociate SSL Policy with VIP") + permissions = ('openstack.services.network',) + help_text = _( "Dissociate SSL Policy from VIP") + + +class DisassociateSSLPolicyStep(workflows.Step): + action_class = DisassociateSSLPolicyAction + depends_on = ("vip_id", "ssl_policy_id", ) + + def contribute(self, data, context): + context = super(DisassociateSSLPolicyStep, self).contribute(data, context) + return context + + +class DisassociateSSLPolicy(workflows.Workflow): + slug = "disassociatesslpolicy" + name = _("Confirm Disassociate SSL Policy") + finalize_button_name = _("Disassociate") + success_message = _('Disassociate.') + failure_message = _('Failed to disassociate.') + success_url = "horizon:project:loadbalancers:index" + default_steps = (DisassociateSSLPolicyStep,) + + def format_status_message(self, message): + return message + + def handle(self, request, context): + try: + api.lbaas.ssl_policy_disassociate(request, **context) + return True + except Exception: + raise + return False + + class AddMonitorStep(workflows.Step): action_class = AddMonitorAction contributes = ("type", "delay", "timeout", "max_retries", @@ -505,6 +701,25 @@ def contribute(self, data, context): return context +class AddSSLpolicyStep(workflows.Step): + action_class = AddSSLpolicyAction + contributes = ("name", "description", "front_end_enabled", "front_end_protocols", "front_end_cipher_suites") + #"back_end_enabled", "back_end_cipher_suites") + + def contribute(self, data, context): + context = super(AddSSLpolicyStep, self).contribute(data, context) + if data: + return context + +class AddSSLcertificateStep(workflows.Step): + action_class = AddSSLcertificateAction + contributes = ("name", "certificate", "passphrase", "certificate_chain") + + def contribute(self, data, context): + context = super(AddSSLcertificateStep, self).contribute(data, context) + if data: + return context + class AddMonitor(workflows.Workflow): slug = "addmonitor" name = _("Add Monitor") @@ -524,6 +739,44 @@ def handle(self, request, context): return False +class AddSSLpolicy(workflows.Workflow): + slug = "addsslpolicy" + name = _("Add SSL Policy") + finalize_button_name = _("Add") + success_message = _('Added SSL policy') + failure_message = _('Unable to add SSL policy') + success_url = "horizon:project:loadbalancers:index" + default_steps = (AddSSLpolicyStep,) + + def handle(self, request, context): + try: + front_end_protocols = ','.join(context['front_end_protocols']) + context['front_end_protocols'] = front_end_protocols + context['ssl_policy_id'] = api.lbaas.ssl_policy_create( + request, **context).get('id') + return True + except Exception: + exceptions.handle(request, _("Unable to add SSL policy.")) + return False + +class AddSSLcertificate(workflows.Workflow): + slug = "addsslcertificate" + name = _("Add SSL Certificate") + finalize_button_name = _("Add") + success_message = _('Added SSL certificate') + failure_message = _('Unable to add SSL certificate') + success_url = "horizon:project:loadbalancers:index" + default_steps = (AddSSLcertificateStep,) + + def handle(self, request, context): + try: + context['ssl_certificate_id'] = api.lbaas.ssl_certificate_create( + request, **context).get('id') + return True + except Exception: + exceptions.handle(request, _("Unable to add SSL certificate.")) + return False + class MonitorMixin(): def _get_monitor_display_name(self, monitor): diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py index f3d88c0b16b..95037d5c2bb 100644 --- a/openstack_dashboard/test/test_data/neutron_data.py +++ b/openstack_dashboard/test/test_data/neutron_data.py @@ -432,6 +432,7 @@ def add_rule_to_group(secgroup, default_only=True): 'session_persistence': {'type': 'APP_COOKIE', 'cookie_name': 'jssessionid'}, 'connection_limit': 10, + 'ssl_policy_id':'b1909408-7d70-4fc8-80c2-5a149eceb369', 'admin_state_up': True} TEST.api_vips.add(vip_dict) TEST.vips.add(lbaas.Vip(vip_dict)) @@ -451,6 +452,7 @@ def add_rule_to_group(secgroup, default_only=True): 'session_persistence': {'type': 'APP_COOKIE', 'cookie_name': 'jssessionid'}, 'connection_limit': 10, + 'ssl_policy_id':'b1909408-7d70-4fc8-80c2-5a149eceb379', 'admin_state_up': True} TEST.api_vips.add(vip_dict) TEST.vips.add(lbaas.Vip(vip_dict))