From 5b7326405aab97fe8c976d1dc16ff8c2a65fa788 Mon Sep 17 00:00:00 2001 From: Jacky Hu Date: Mon, 12 Mar 2018 18:14:23 +0800 Subject: [PATCH] Add l7 support Enable users to create l7 policies and rules on a listener. Change-Id: I29130311346c8627473222491a76777a9dc95f52 --- octavia_dashboard/api/rest/lbaasv2.py | 227 ++++++++++++++++ .../openstack-service-api/lbaasv2.service.js | 210 ++++++++++++++- .../lbaasv2.service.spec.js | 101 +++++++ .../actions/create/create.action.service.js | 74 ++++++ .../create/create.action.service.spec.js | 65 +++++ .../actions/create/wizard.controller.js | 45 ++++ .../actions/create/wizard.controller.spec.js | 63 +++++ .../actions/delete/delete.action.service.js | 135 ++++++++++ .../delete/delete.action.service.spec.js | 103 ++++++++ .../actions/edit/edit.action.service.js | 69 +++++ .../actions/edit/edit.action.service.spec.js | 55 ++++ .../actions/edit/wizard.controller.js | 60 +++++ .../actions/edit/wizard.controller.spec.js | 77 ++++++ .../l7policies/details/detail.controller.js | 109 ++++++++ .../details/detail.controller.spec.js | 112 ++++++++ .../lbaasv2/l7policies/details/detail.html | 61 +++++ .../lbaasv2/l7policies/details/drawer.html | 9 + .../lbaasv2/l7policies/l7policies.module.js | 177 +++++++++++++ .../l7policies/l7policies.module.spec.js | 67 +++++ .../actions/create/create.action.service.js | 74 ++++++ .../create/create.action.service.spec.js | 65 +++++ .../actions/create/wizard.controller.js | 45 ++++ .../actions/create/wizard.controller.spec.js | 63 +++++ .../actions/delete/delete.action.service.js | 137 ++++++++++ .../delete/delete.action.service.spec.js | 103 ++++++++ .../actions/edit/edit.action.service.js | 69 +++++ .../actions/edit/edit.action.service.spec.js | 55 ++++ .../l7rules/actions/edit/wizard.controller.js | 60 +++++ .../actions/edit/wizard.controller.spec.js | 77 ++++++ .../l7rules/details/detail.controller.js | 119 +++++++++ .../l7rules/details/detail.controller.spec.js | 114 ++++++++ .../lbaasv2/l7rules/details/detail.html | 54 ++++ .../lbaasv2/l7rules/details/drawer.html | 8 + .../project/lbaasv2/l7rules/l7rules.module.js | 181 +++++++++++++ .../lbaasv2/l7rules/l7rules.module.spec.js | 67 +++++ .../project/lbaasv2/lbaasv2.module.js | 103 ++++++++ .../project/lbaasv2/lbaasv2.module.spec.js | 115 ++++++++ .../lbaasv2/listeners/details/detail.html | 6 + .../actions/create/wizard.controller.js | 6 +- .../loadbalancers/loadbalancers.service.js | 83 ++++++ .../loadbalancers.service.spec.js | 55 ++++ .../workflow/l7policy/l7policy.controller.js | 52 ++++ .../l7policy/l7policy.controller.spec.js | 37 +++ .../workflow/l7policy/l7policy.help.html | 38 +++ .../lbaasv2/workflow/l7policy/l7policy.html | 104 ++++++++ .../workflow/l7rule/l7rule.controller.js | 49 ++++ .../workflow/l7rule/l7rule.controller.spec.js | 37 +++ .../lbaasv2/workflow/l7rule/l7rule.help.html | 76 ++++++ .../lbaasv2/workflow/l7rule/l7rule.html | 92 +++++++ .../project/lbaasv2/workflow/model.service.js | 110 ++++++++ .../lbaasv2/workflow/model.service.spec.js | 250 +++++++++++++++++- .../lbaasv2/workflow/workflow.service.js | 14 + .../lbaasv2/workflow/workflow.service.spec.js | 4 +- .../add-l7-support-05a790bc2965c38f.yaml | 4 + 54 files changed, 4338 insertions(+), 7 deletions(-) create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/drawer.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/drawer.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.help.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.spec.js create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.help.html create mode 100644 octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.html create mode 100644 releasenotes/notes/add-l7-support-05a790bc2965c38f.yaml diff --git a/octavia_dashboard/api/rest/lbaasv2.py b/octavia_dashboard/api/rest/lbaasv2.py index c5ca48e5..71fd373c 100644 --- a/octavia_dashboard/api/rest/lbaasv2.py +++ b/octavia_dashboard/api/rest/lbaasv2.py @@ -180,6 +180,47 @@ def create_listener(request, **kwargs): return _get_sdk_object_dict(listener) +def create_l7_policy(request, **kwargs): + """Create a new l7 policy. + + """ + data = request.DATA + + conn = _get_sdk_connection(request) + l7_policy = conn.load_balancer.create_l7_policy( + action=data['l7policy']['action'], + admin_state_up=data['l7policy'].get('admin_state_up'), + description=data['l7policy'].get('description'), + listener_id=kwargs['listener_id'], + name=data['l7policy'].get('name'), + position=data['l7policy'].get('position'), + redirect_pool_id=data['l7policy'].get('redirect_pool_id'), + redirect_url=data['l7policy'].get('redirect_url'), + ) + + return _get_sdk_object_dict(l7_policy) + + +def create_l7_rule(request, **kwargs): + """Create a new l7 rule. + + """ + data = request.DATA + + conn = _get_sdk_connection(request) + l7_rule = conn.load_balancer.create_l7_rule( + admin_state_up=data['l7rule'].get('admin_state_up'), + compare_type=data['l7rule']['compare_type'], + invert=data['l7rule'].get('invert'), + key=data['l7rule'].get('key'), + l7_policy=kwargs['l7_policy_id'], + type=data['l7rule']['type'], + rule_value=data['l7rule']['rule_value'], + ) + + return _get_sdk_object_dict(l7_rule) + + def create_pool(request, **kwargs): """Create a new pool. @@ -369,6 +410,50 @@ def update_listener(request, **kwargs): return _get_sdk_object_dict(listener) +def update_l7_policy(request, **kwargs): + """Update a l7 policy. + + """ + data = request.DATA + l7_policy_id = data['l7policy'].get('id') + + conn = _get_sdk_connection(request) + l7_policy = conn.load_balancer.update_l7_policy( + action=data['l7policy']['action'], + admin_state_up=data['l7policy'].get('admin_state_up'), + description=data['l7policy'].get('description'), + l7_policy=l7_policy_id, + name=data['l7policy'].get('name'), + position=data['l7policy'].get('position'), + redirect_pool_id=data['l7policy'].get('redirect_pool_id'), + redirect_url=data['l7policy'].get('redirect_url'), + ) + + return _get_sdk_object_dict(l7_policy) + + +def update_l7_rule(request, **kwargs): + """Update a l7 rule. + + """ + data = request.DATA + l7_rule_id = data['l7rule'].get('id') + + conn = _get_sdk_connection(request) + l7_rule = conn.load_balancer.update_l7_rule( + admin_state_up=data['l7rule'].get('admin_state_up'), + compare_type=data['l7rule']['compare_type'], + invert=data['l7rule'].get('invert'), + key=data['l7rule'].get('key'), + l7_policy=kwargs['l7_policy_id'], + l7rule=l7_rule_id, + type=data['l7rule']['type'], + rule_value=data['l7rule']['rule_value'], + ) + + return _get_sdk_object_dict(l7_rule) + + def update_pool(request, **kwargs): """Update a pool. @@ -674,6 +759,148 @@ class Listener(generic.View): conn.load_balancer.delete_listener(listener_id, ignore_missing=True) +@urls.register +class L7Policies(generic.View): + """API for load balancer l7 policies. + + """ + url_regex = r'lbaas/l7policies/$' + + @rest_utils.ajax() + def get(self, request): + """List of l7 policies for the current project. + + The listing result is an object with property "items". + """ + listener_id = request.GET.get('listenerId') + conn = _get_sdk_connection(request) + l7_policy_list = _sdk_object_to_list(conn.load_balancer.l7_policies( + listener_id=listener_id)) + return {'items': l7_policy_list} + + @rest_utils.ajax() + def post(self, request): + """Create a new l7 policy. + + Creates a new l7 policy as well as other optional resources such as + l7 rules. + """ + kwargs = {'listener_id': request.DATA.get('parentResourceId')} + return create_l7_policy(request, **kwargs) + + +@urls.register +class L7Policy(generic.View): + """API for retrieving a single l7 policy. + + """ + url_regex = r'lbaas/l7policies/(?P[^/]+)/$' + + @rest_utils.ajax() + def get(self, request, l7_policy_id): + """Get a specific l7 policy. + + If the param 'includeChildResources' is passed in as a truthy value, + the details of all resources that exist under the l7 policy will be + returned along with the l7 policy details. + + http://localhost/api/lbaas/l7policies/cc758c90-3d98-4ea1-af44-aab405c9c915 + """ + conn = _get_sdk_connection(request) + l7_policy = conn.load_balancer.find_l7_policy(l7_policy_id) + l7_policy = _get_sdk_object_dict(l7_policy) + + if request.GET.get('includeChildResources'): + resources = {} + + if l7_policy.get('rules'): + l7_rules_list = _sdk_object_to_list( + conn.load_balancer.l7_rules(l7_policy_id)) + l7_policy['rules'] = l7_rules_list + + resources['l7policy'] = l7_policy + + return resources + else: + return l7_policy + + @rest_utils.ajax() + def put(self, request, l7_policy_id): + """Edit a l7 policy as well as any resources below it. + + """ + kwargs = {'l7_policy_id': l7_policy_id} + update_l7_policy(request, **kwargs) + + @rest_utils.ajax() + def delete(self, request, l7_policy_id): + """Delete a specific l7 policy. + + http://localhost/api/lbaas/l7policies/cc758c90-3d98-4ea1-af44-aab405c9c915 + """ + conn = _get_sdk_connection(request) + conn.load_balancer.delete_l7_policy(l7_policy_id) + + +@urls.register +class L7Rules(generic.View): + """API for load balancer l7 rules. + + """ + url_regex = r'lbaas/l7policies/(?P[^/]+)/l7rules/$' + + @rest_utils.ajax() + def get(self, request, l7_policy_id): + """List of l7 rules for the current project. + + The listing result is an object with property "items". + """ + conn = _get_sdk_connection(request) + l7_rule_list = _sdk_object_to_list(conn.load_balancer.l7_rules( + l7_policy_id)) + return {'items': l7_rule_list} + + @rest_utils.ajax() + def post(self, request, l7_policy_id): + """Create a new l7 rule. + + Creates a new l7 rule as well as other optional resources such as + l7 rules. + """ + kwargs = {'l7_policy_id': l7_policy_id} + return create_l7_rule(request, **kwargs) + + +@urls.register +class L7Rule(generic.View): + """API for retrieving a single l7 rule. + + """ + url_regex = ( + r'lbaas/l7policies/(?P[^/]+)' + r'/l7rules/(?P[^/]+)/$' + ) + + @rest_utils.ajax() + def get(self, request, l7_rule_id, l7_policy_id): + """Get a specific l7 rule.""" + conn = _get_sdk_connection(request) + l7_rule = conn.load_balancer.find_l7_rule(l7_rule_id, l7_policy_id) + return _get_sdk_object_dict(l7_rule) + + @rest_utils.ajax() + def put(self, request, l7_rule_id, l7_policy_id): + """Edit a specific l7 rule.""" + kwargs = {'l7_rule_id': l7_rule_id, 'l7_policy_id': l7_policy_id} + update_l7_rule(request, **kwargs) + + @rest_utils.ajax() + def delete(self, request, l7_rule_id, l7_policy_id): + """Delete a specific l7 rule.""" + conn = _get_sdk_connection(request) + conn.load_balancer.delete_l7_rule(l7_rule_id, l7_policy_id) + + @urls.register class Pools(generic.View): """API for load balancer pools. diff --git a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js index 6ebd55c4..ae2d410c 100644 --- a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js +++ b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js @@ -46,6 +46,16 @@ createListener: createListener, editListener: editListener, deleteListener: deleteListener, + getL7Policies: getL7Policies, + getL7Policy: getL7Policy, + createL7Policy: createL7Policy, + editL7Policy: editL7Policy, + deleteL7Policy: deleteL7Policy, + getL7Rules: getL7Rules, + getL7Rule: getL7Rule, + createL7Rule: createL7Rule, + editL7Rule: editL7Rule, + deleteL7Rule: deleteL7Rule, getPools: getPools, getPool: getPool, createPool: createPool, @@ -108,8 +118,8 @@ * @description * Delete a single load balancer by ID * @param {string} id - * @param {boolean} quiet * Specifies the id of the load balancer to delete. + * @param {boolean} quiet */ function deleteLoadBalancer(id, quiet) { @@ -231,8 +241,8 @@ * @description * Delete a single listener by ID * @param {string} id - * @param {boolean} quiet * Specifies the id of the listener to delete. + * @param {boolean} quiet */ function deleteListener(id, quiet) { @@ -333,8 +343,8 @@ * @description * Delete a single pool by ID * @param {string} id - * @param {boolean} quiet * Specifies the id of the pool to delete. + * @param {boolean} quiet */ function deletePool(id, quiet) { @@ -344,6 +354,198 @@ }); } + // L7 Policies + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getL7Policies + * @description + * Get the list of l7 policies. + * If a listener ID is passed as a parameter, the returning list of + * l7 policies will be filtered to include only those l7 policies under the + * specified listener. + * @param {string} listenerId + * Specifies the id of the listener to request l7policies for. + * + * The listing result is an object with property "items". Each item is + * a l7 policy. + */ + + function getL7Policies(listenerId) { + var params = $.extend({}, + { + listenerId: listenerId + } + ); + if (!$.isEmptyObject(params)) { + params = { params: params }; + } + return apiService.get('/api/lbaas/l7policies/', params) + .error(function () { + toastService.add('error', gettext('Unable to retrieve l7 policies.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getL7Policy + * @description + * Get a single L7Policy by ID. + * @param {string} id + * Specifies the id of the l7 policy to request. + * @param {boolean} includeChildResources + * If truthy, all child resources below the l7 policy will be included in the response. + */ + + function getL7Policy(id, includeChildResources) { + var params = includeChildResources + ? {params: {includeChildResources: includeChildResources}} + : {}; + return apiService.get('/api/lbaas/l7policies/' + id + '/', params) + .error(function () { + toastService.add('error', gettext('Unable to retrieve l7 policy.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.createL7Policy + * @description + * Create a new l7 policy + * @param {object} spec + * Specifies the data used to create the new l7 policy. + */ + + function createL7Policy(spec) { + return apiService.post('/api/lbaas/l7policies/', spec) + .error(function () { + toastService.add('error', gettext('Unable to create l7 policy.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.editL7Policy + * @description + * Edit a l7 policy + * @param {string} id + * Specifies the id of the l7 policy to update. + * @param {object} spec + * Specifies the data used to update the l7 policy. + */ + + function editL7Policy(id, spec) { + return apiService.put('/api/lbaas/l7policies/' + id + '/', spec) + .error(function () { + toastService.add('error', gettext('Unable to update l7 policy.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.deleteL7Policy + * @description + * Delete a single l7 policy by ID + * @param {string} id + * Specifies the id of the l7 policy to delete. + * @param {boolean} quiet + */ + + function deleteL7Policy(id, quiet) { + var promise = apiService.delete('/api/lbaas/l7policies/' + id + '/'); + return quiet ? promise : promise.error(function () { + toastService.add('error', gettext('Unable to delete l7 policy.')); + }); + } + + // L7 Rules + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getL7Rules + * @description + * Get the list of l7 rules under the specified l7 policy. + * @param {string} l7policyId + * Specifies the id of the l7 policy to request l7rules for. + * + * The listing result is an object with property "items". + * Each item is a l7 rule. + */ + + function getL7Rules(l7policyId) { + return apiService.get('/api/lbaas/l7policies/' + l7policyId + '/l7rules/') + .error(function () { + toastService.add('error', gettext('Unable to retrieve l7 rules.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.getL7Rule + * @description + * Get a single L7Rule by ID. + * @param {string} l7policyId + * Specifies the id of the l7 policy the l7 rule belongs to. + * @param {string} l7ruleId + * Specifies the id of the l7 rule to request. + */ + + function getL7Rule(l7policyId, l7ruleId) { + return apiService.get('/api/lbaas/l7policies/' + l7policyId + '/l7rules/' + l7ruleId + '/') + .error(function () { + toastService.add('error', gettext('Unable to retrieve l7 rule.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.createL7Rule + * @description + * Create a new l7 rule + * @param {string} l7policyId + * Specifies the id of the l7 policy the l7 rule belongs to. + * @param {object} spec + * Specifies the data used to create the new l7 rule. + */ + + function createL7Rule(l7policyId, spec) { + return apiService.post('/api/lbaas/l7policies/' + l7policyId + '/l7rules/', spec) + .error(function () { + toastService.add('error', gettext('Unable to create l7 rule.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.editL7Rule + * @description + * Edit a l7 rule + * @param {string} l7policyId + * Specifies the id of the l7 policy the l7 rule belongs to. + * @param {string} l7ruleId + * Specifies the id of the l7 rule to update. + * @param {object} spec + * Specifies the data used to update the l7 rule. + */ + + function editL7Rule(l7policyId, l7ruleId, spec) { + return apiService.put('/api/lbaas/l7policies/' + l7policyId + + '/l7rules/' + l7ruleId + '/', spec) + .error(function () { + toastService.add('error', gettext('Unable to update l7 rule.')); + }); + } + + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.deleteL7Rule + * @description + * Delete a single l7 rule by ID + * @param {string} l7policyId + * Specifies the id of the l7 policy the l7 rule belongs to. + * @param {string} l7ruleId + * Specifies the id of the l7 rule to delete. + * @param {boolean} quiet + */ + + function deleteL7Rule(l7policyId, l7ruleId, quiet) { + var promise = apiService.delete('/api/lbaas/l7policies/' + l7policyId + + '/l7rules/' + l7ruleId + '/'); + return quiet ? promise : promise.error(function () { + toastService.add('error', gettext('Unable to delete l7 rule.')); + }); + } + // Members /** @@ -493,8 +695,8 @@ * @description * Delete a single health monitor by ID * @param {string} id - * @param {boolean} quiet * Specifies the id of the health monitor to delete. + * @param {boolean} quiet */ function deleteHealthMonitor(id, quiet) { diff --git a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js index 118b83fd..4a6e4e86 100644 --- a/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js +++ b/octavia_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js @@ -90,6 +90,65 @@ error: 'Unable to retrieve listener.', testInput: [ '1234', false ] }, + { + func: 'getL7Policies', + method: 'get', + path: '/api/lbaas/l7policies/', + error: 'Unable to retrieve l7 policies.', + testInput: [ '1234' ], + data: { params: { listenerId: '1234' } } + }, + { + func: 'getL7Policies', + method: 'get', + path: '/api/lbaas/l7policies/', + data: {}, + error: 'Unable to retrieve l7 policies.' + }, + { + func: 'getL7Policy', + method: 'get', + path: '/api/lbaas/l7policies/1234/', + data: { params: { includeChildResources: true } }, + error: 'Unable to retrieve l7 policy.', + testInput: [ '1234', true ] + }, + { + func: 'getL7Policy', + method: 'get', + path: '/api/lbaas/l7policies/1234/', + data: {}, + error: 'Unable to retrieve l7 policy.', + testInput: [ '1234', false ] + }, + { + func: 'deleteL7Policy', + method: 'delete', + path: '/api/lbaas/l7policies/1234/', + error: 'Unable to delete l7 policy.', + testInput: [ '1234' ] + }, + { + func: 'getL7Rules', + method: 'get', + path: '/api/lbaas/l7policies/1234/l7rules/', + error: 'Unable to retrieve l7 rules.', + testInput: [ '1234' ] + }, + { + func: 'getL7Rule', + method: 'get', + path: '/api/lbaas/l7policies/1234/l7rules/5678/', + error: 'Unable to retrieve l7 rule.', + testInput: [ '1234', '5678' ] + }, + { + func: 'deleteL7Rule', + method: 'delete', + path: '/api/lbaas/l7policies/1234/l7rules/5678/', + error: 'Unable to delete l7 rule.', + testInput: [ '1234', '5678' ] + }, { func: 'getPools', method: 'get', @@ -249,6 +308,38 @@ error: 'Unable to delete listener.', testInput: [ '1234' ] }, + { + func: 'createL7Policy', + method: 'post', + path: '/api/lbaas/l7policies/', + error: 'Unable to create l7 policy.', + data: { name: 'l7policy-1' }, + testInput: [ { name: 'l7policy-1' } ] + }, + { + func: 'editL7Policy', + method: 'put', + path: '/api/lbaas/l7policies/1234/', + error: 'Unable to update l7 policy.', + data: { name: 'l7policy-1' }, + testInput: [ '1234', { name: 'l7policy-1' } ] + }, + { + func: 'createL7Rule', + method: 'post', + path: '/api/lbaas/l7policies/1234/l7rules/', + error: 'Unable to create l7 rule.', + data: { name: 'l7rule-1' }, + testInput: [ '1234', { name: 'l7rule-1' } ] + }, + { + func: 'editL7Rule', + method: 'put', + path: '/api/lbaas/l7policies/1234/l7rules/5678/', + error: 'Unable to update l7 rule.', + data: { name: 'l7rule-1' }, + testInput: [ '1234', '5678', { name: 'l7rule-1' } ] + }, { func: 'createPool', method: 'post', @@ -301,6 +392,16 @@ expect(service.deleteListener("whatever", true)).toBe("promise"); }); + it('supresses the error if instructed for deleteL7Policy', function() { + spyOn(apiService, 'delete').and.returnValue("promise"); + expect(service.deleteL7Policy("whatever", true)).toBe("promise"); + }); + + it('supresses the error if instructed for deleteL7Rule', function() { + spyOn(apiService, 'delete').and.returnValue("promise"); + expect(service.deleteL7Rule("whatever", "whatever", true)).toBe("promise"); + }); + it('supresses the error if instructed for deletePool', function() { spyOn(apiService, 'delete').and.returnValue("promise"); expect(service.deletePool("whatever", true)).toBe("promise"); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.js new file mode 100644 index 00000000..b0800cab --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.js @@ -0,0 +1,74 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies') + .factory('horizon.dashboard.project.lbaasv2.l7policies.actions.create', createService); + + createService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7policies.resourceType', + 'horizon.framework.util.actions.action-result.service', + '$q', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7policies.actions.createService + * + * @description + * Provides the service for creating a l7policy resource. + * + * @param resourceType The l7policy resource type. + * @param actionResultService The horizon action result service. + * @param $q The angular service for promises. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns The l7policy create service. + */ + + function createService( + resourceType, actionResultService, + $q, workflowModal, policy, gettext + ) { + return workflowModal.init({ + controller: 'CreateL7PolicyWizardController', + message: gettext('A new l7 policy is being created.'), + handle: handle, + allowed: allowed + }); + + ////////////// + + function allowed() { + return $q.all([ + policy.ifAllowed({ rules: [['neutron', 'create_l7policy']] }) + ]); + } + + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; + } + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.spec.js new file mode 100644 index 00000000..2d7ccd64 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/create.action.service.spec.js @@ -0,0 +1,65 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 Create l7policy Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'listener1' } }); + } + } + }; + } + }); + $provide.value('$routeParams', {}); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.l7policies.actions.create'); + })); + + it('should not allow creating a l7policy if listenerId is not present', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var allowed = service.allowed(); + permissionShouldFail(allowed); + }); + + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); + }); + + function permissionShouldFail(permissions) { + permissions.then( + function() { + expect(false).toBe(true); + }, + function() { + expect(true).toBe(true); + }); + } + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.js new file mode 100644 index 00000000..05d2160f --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.js @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies') + .controller('CreateL7PolicyWizardController', CreateL7PolicyWizardController); + + CreateL7PolicyWizardController.$inject = [ + '$scope', + '$routeParams', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + function CreateL7PolicyWizardController($scope, $routeParams, model, workflowService, gettext) { + var loadbalancerId = $routeParams.loadbalancerId; + var listenerId = $routeParams.listenerId; + var scope = $scope; + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService( + gettext('Create L7 Policy'), + 'fa fa-cloud-download', + ['l7policy'] + ); + scope.model.initialize('l7policy', false, loadbalancerId, listenerId); + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.spec.js new file mode 100644 index 00000000..6dede502 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/create/wizard.controller.spec.js @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Create L7Policy Wizard Controller', function() { + var ctrl; + var model = { + submit: function() { + return 'created'; + }, + initialize: angular.noop + }; + var workflow = function() { + return 'foo'; + }; + var scope = { + launchContext: {id: '1234'} + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflow); + })); + beforeEach(inject(function ($controller) { + spyOn(model, 'initialize'); + ctrl = $controller('CreateL7PolicyWizardController', { $scope: scope }); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalled(); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe('foo'); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBeDefined(); + expect(scope.submit()).toBe('created'); + }); + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.js new file mode 100644 index 00000000..d1260653 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.js @@ -0,0 +1,135 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies') + .factory('horizon.dashboard.project.lbaasv2.l7policies.actions.delete', deleteService); + + deleteService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7policies.resourceType', + 'horizon.framework.util.actions.action-result.service', + '$location', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.app.core.openstack-service-api.lbaasv2', + 'horizon.framework.util.i18n.gettext', + 'horizon.app.core.openstack-service-api.policy' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7policies.actions.deleteService + * + * @description + * Brings up the delete l7policy confirmation modal dialog. + * On submit, deletes selected l7policy. + * On cancel, does nothing. + * + * @param resourceType The l7policy resource type. + * @param actionResultService The horizon action result service. + * @param $location The angular $location service. + * @param deleteModal The horizon delete modal service. + * @param api The LBaaS v2 API service. + * @param gettext The horizon gettext function for translation. + * @param policy The horizon policy service. + * + * @returns The l7policy delete service. + */ + + function deleteService( + resourceType, actionResultService, $location, + deleteModal, api, gettext, policy + ) { + var loadbalancerId, listenerId; + + var service = { + perform: perform, + allowed: allowed, + deleteResult: deleteResult // exposed just for testing + }; + + return service; + + ////////////// + + function allowed(/*item*/) { + // This rule is made up and should therefore always pass. I assume at some point there + // will be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'delete_l7policy']] }); + } + + function perform(items, scope) { + var context = { }; + var l7policies = angular.isArray(items) ? items : [items]; + context.labels = labelize(l7policies.length); + context.deleteEntity = deleteItem; + l7policies.map(function(item) { + loadbalancerId = item.loadbalancerId; + listenerId = item.listenerId; + }); + return deleteModal.open(scope, l7policies, context).then(deleteResult); + } + + function labelize(count) { + return { + title: ngettext( + 'Confirm Delete L7 Policy', + 'Confirm Delete L7 Policies', count), + + message: ngettext( + 'You have selected "%s". Deleted L7 Policy is not recoverable.', + 'You have selected "%s". Deleted L7 Policies are not recoverable.', count), + + submit: ngettext( + 'Delete L7 Policy', + 'Delete L7 Policies', count), + + success: ngettext( + 'Deleted L7 Policy: %s.', + 'Deleted L7 Policies: %s.', count), + + error: ngettext( + 'Unable to delete L7 Policy: %s.', + 'Unable to delete L7 Policies: %s.', count) + }; + } + + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer/' + loadbalancerId + + '/listeners/' + listenerId; + $location.path(path); + } + return actionResult.result; + } + + function deleteItem(id) { + return api.deleteL7Policy(id, true); + } + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.spec.js new file mode 100644 index 00000000..ed59d3fe --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/delete/delete.action.service.spec.js @@ -0,0 +1,103 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Policy Delete Service', function() { + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module('horizon.framework')); + + var deleteModalService, service, lbaasv2API, policyAPI, $location; + + beforeEach(inject(function($injector) { + service = $injector.get('horizon.dashboard.project.lbaasv2.l7policies.actions.delete'); + lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + deleteModalService = $injector.get('horizon.framework.widgets.modal.deleteModalService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); + })); + + describe('perform method', function() { + beforeEach(function () { + // just need for this to return something that looks like a promise but does nothing + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + }); + + it('should open the modal with correct label', function () { + service.perform({name: 'spam'}); + var labels = deleteModalService.open.calls.argsFor(0)[2].labels; + expect(deleteModalService.open).toHaveBeenCalled(); + angular.forEach(labels, function eachLabel(label) { + expect(label.toLowerCase()).toContain('l7 policy'); + }); + }); + + it('should open the delete modal with correct entities', function () { + service.perform([{name: 'one'}, {name: 'two'}]); + var entities = deleteModalService.open.calls.argsFor(0)[1]; + expect(deleteModalService.open).toHaveBeenCalled(); + expect(entities.length).toEqual(2); + }); + + it('should pass in a function that deletes a l7 policy', function () { + spyOn(lbaasv2API, 'deleteL7Policy').and.callFake(angular.noop); + service.perform({id: 1, name: 'one'}); + var contextArg = deleteModalService.open.calls.argsFor(0)[2]; + var deleteFunction = contextArg.deleteEntity; + deleteFunction(1); + expect(lbaasv2API.deleteL7Policy).toHaveBeenCalledWith(1, true); + }); + }); + + it('should handle the action result properly', function() { + spyOn($location, 'path'); + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + spyOn(lbaasv2API, 'deleteL7Policy').and.callFake(angular.noop); + service.perform({loadbalancerId: 1, listenerId: 2, id: 1, name: 'one'}); + var result = service.deleteResult({ + fail: [], + pass: [{ + context: { + id: 1 + } + }] + }); + var path = 'project/load_balancer/1/listeners/2'; + expect($location.path).toHaveBeenCalledWith(path); + expect(result.deleted[0].id).toBe(1); + result = service.deleteResult({ + pass: [], + fail: [{ + context: { + id: 1 + } + }] + }); + expect(result.failed[0].id).toBe(1); + }); + + describe('allow method', function() { + it('should use default policy if batch action', function () { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of delete + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.js new file mode 100644 index 00000000..963f4cf3 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.js @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies') + .factory('horizon.dashboard.project.lbaasv2.l7policies.actions.edit', editService); + + editService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7policies.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7policies.actions.editService + * + * @description + * Provides the service for editing a l7policy resource. + * + * @param resourceType The l7policy resource type. + * @param actionResultService The horizon action result service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns The l7policy edit service. + */ + + function editService( + resourceType, actionResultService, workflowModal, policy, gettext + ) { + + return workflowModal.init({ + controller: 'EditL7PolicyWizardController', + message: gettext('The l7policy has been updated.'), + handle: handle, + allowed: allowed + }); + + function allowed(/*item*/) { + return policy.ifAllowed({ rules: [['neutron', 'update_l7policy']] }); + } + + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.l7policy.id) + .result; + } + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.spec.js new file mode 100644 index 00000000..89d7fe90 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/edit.action.service.spec.js @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 Edit L7Policy Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'l7policy1' } }); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.l7policies.actions.edit'); + })); + + it('should check policy to allow editing a l7policy', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_l7policy']]}); + }); + + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {l7policy: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.js new file mode 100644 index 00000000..b1a4f1c7 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.js @@ -0,0 +1,60 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.loadbalancers') + .controller('EditL7PolicyWizardController', EditL7PolicyWizardController); + + EditL7PolicyWizardController.$inject = [ + '$scope', + '$routeParams', + '$q', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc controller + * @name EditL7PolicyWizardController + * + * @description + * Controller for the LBaaS v2 edit l7policy wizard. + * + * @param $scope The angular scope object. + * @param $routeParams The angular $routeParams service. + * @param $q The angular service for promises. + * @param model The LBaaS V2 workflow model service. + * @param workflowService The LBaaS V2 workflow service. + * @param gettext The horizon gettext function for translation. + * @returns undefined + */ + + function EditL7PolicyWizardController($scope, $routeParams, $q, model, workflowService, gettext) { + var scope = $scope; + var loadbalancerId = $routeParams.loadbalancerId; + var listenerId = $routeParams.listenerId; + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService( + gettext('Update L7 Policy'), + 'fa fa-pencil', ['l7policy']); + scope.model.initialize('l7policy', scope.launchContext.id, loadbalancerId, listenerId); + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.spec.js new file mode 100644 index 00000000..a985d954 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/actions/edit/wizard.controller.spec.js @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Edit L7Policy Wizard Controller', function() { + var ctrl, workflowSpy, $q, scope; + var model = { + submit: function() { + return 'updated'; + }, + initialize: function() { + var defer = $q.defer(); + defer.resolve(); + return defer.promise; + } + }; + var workflow = { + steps: [{id: 'l7policy'}], + append: angular.noop + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + workflowSpy = jasmine.createSpy('workflow').and.returnValue(workflow); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflowSpy); + })); + beforeEach(inject(function ($controller, $injector) { + $q = $injector.get('$q'); + scope = $injector.get('$rootScope').$new(); + scope.launchContext = { id: 'l7policyId' }; + spyOn(model, 'initialize').and.callThrough(); + ctrl = $controller('EditL7PolicyWizardController', { + $scope: scope, + $routeParams: {loadbalancerId: 'loadbalancerId', listenerId: 'listenerId'}}); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalledWith( + 'l7policy', 'l7policyId', 'loadbalancerId', 'listenerId'); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe(workflow); + }); + + it('initializes workflow with correct properties', function() { + expect(workflowSpy).toHaveBeenCalledWith('Update L7 Policy', + 'fa fa-pencil', ['l7policy']); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBe(model.submit); + expect(scope.submit()).toBe('updated'); + }); + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.js new file mode 100644 index 00000000..b120648f --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.js @@ -0,0 +1,109 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies') + .controller('L7PolicyDetailController', L7PolicyDetailController); + + L7PolicyDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'l7policy', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.l7policies.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name L7PolicyDetailController + * + * @description + * Controller for the LBaaS v2 l7policy detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param l7policy The l7policy object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The load balancer resource type. + * @param typeRegistry The horizon type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function L7PolicyDetailController( + loadbalancer, listener, l7policy, loadBalancersService, + resourceType, typeRegistry, spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.l7policyAction = loadBalancersService.l7policyAction; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.l7policy = l7policy; + ctrl.listFunctionExtraParams = { + loadbalancerId: ctrl.loadbalancer.id, + listenerId: ctrl.listener.id, + l7policyId: ctrl.l7policy.id + }; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.identifier = l7policy.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.l7policy = response.data; + ctrl.l7policy.loadbalancerId = ctrl.loadbalancer.id; + ctrl.l7policy.listenerId = ctrl.listener.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load(ctrl.context.identifier); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.spec.js new file mode 100644 index 00000000..92caada1 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.controller.spec.js @@ -0,0 +1,112 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Policy Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('L7PolicyDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + l7policy: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + expect(ctrl.l7policy).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({failed: [], deleted: []}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: []}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.html new file mode 100644 index 00000000..b7ce90cf --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/detail.html @@ -0,0 +1,61 @@ + + + +
+ + +
+
+ + + + +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/drawer.html b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/drawer.html new file mode 100644 index 00000000..86b3251a --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/details/drawer.html @@ -0,0 +1,9 @@ + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.js new file mode 100644 index 00000000..9a28f8ff --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.js @@ -0,0 +1,177 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.project.lbaasv2.l7policies + * + * @description + * Provides the services and widgets required to support and display the project l7 policies + * for the load balancers v2 panel. + */ + + angular + .module('horizon.dashboard.project.lbaasv2.l7policies', []) + .constant('horizon.dashboard.project.lbaasv2.l7policies.resourceType', + 'OS::Octavia::L7Policy') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.l7policies.actions.create', + 'horizon.dashboard.project.lbaasv2.l7policies.actions.edit', + 'horizon.dashboard.project.lbaasv2.l7policies.actions.delete', + 'horizon.dashboard.project.lbaasv2.l7policies.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var l7policyResourceType = registry.getResourceType(resourceType); + + l7policyResourceType + .setNames(gettext('L7 Policy'), gettext('L7 Policies')) + .setSummaryTemplateUrl(basePath + 'l7policies/details/drawer.html') + .setProperties(l7policyProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getL7PoliciesPromise) + .setLoadFunction(loadBalancerService.getL7PolicyPromise) + .tableColumns + .append({ + id: 'name', + priority: 1, + urlFunction: loadBalancerService.getL7PolicyDetailsPath + }) + .append({ + id: 'position', + sortDefault: true, + priority: 1 + }) + .append({ + id: 'action', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + l7policyResourceType.itemActions + .append({ + id: 'l7policyEdit', + service: editService, + template: { + text: gettext('Edit L7 Policy') + } + }) + .append({ + id: 'l7policyDelete', + service: deleteService, + template: { + text: gettext('Delete L7 Policy'), + type: 'delete' + } + }); + + l7policyResourceType.globalActions + .append({ + id: 'l7policyCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create L7 Policy') + } + }); + + l7policyResourceType.batchActions + .append({ + id: 'l7policyBatchDelete', + service: deleteService, + template: { + text: gettext('Delete L7 Policies'), + type: 'delete-selected' + } + }); + } + + function l7policyProperties(loadBalancerService) { + return { + id: gettext('ID'), + name: { + label: gettext('Name'), + filters: ['noName'] + }, + description: { + label: gettext('Description'), + filters: ['noValue'] + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + action: { + label: gettext('Action'), + values: loadBalancerService.l7policyAction + }, + redirect_url: { + label: gettext('Redirect URL'), + filters: ['noName'] + }, + redirect_pool_id: { + label: gettext('Redirect Pool ID'), + filters: ['noName'] + }, + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + }, + position: gettext('Position'), + listener_id: gettext('Listener ID'), + rules: gettext('Rules') + }; + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.spec.js new file mode 100644 index 00000000..9a33b1ff --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7policies/l7policies.module.spec.js @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Policies Module', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.project.lbaasv2.l7policies')).toBeDefined(); + }); + }); + + describe('LBaaS v2 L7Policies Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.l7policies.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'l7policyEdit')).toBe(true); + expect(actionHasId(actions, 'l7policyDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'l7policyCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'l7policyBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.js new file mode 100644 index 00000000..aa452427 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.js @@ -0,0 +1,74 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules') + .factory('horizon.dashboard.project.lbaasv2.l7rules.actions.create', createService); + + createService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7rules.resourceType', + 'horizon.framework.util.actions.action-result.service', + '$q', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7rules.actions.createService + * + * @description + * Provides the service for creating a l7rule resource. + * + * @param resourceType The l7rule resource type. + * @param actionResultService The horizon action result service. + * @param $q The angular service for promises. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns The l7rule create service. + */ + + function createService( + resourceType, actionResultService, + $q, workflowModal, policy, gettext + ) { + return workflowModal.init({ + controller: 'CreateL7RuleWizardController', + message: gettext('A new l7 policy is being created.'), + handle: handle, + allowed: allowed + }); + + ////////////// + + function allowed() { + return $q.all([ + policy.ifAllowed({ rules: [['neutron', 'create_l7rule']] }) + ]); + } + + function handle(response) { + return actionResultService.getActionResult() + .created(resourceType, response.data.id) + .result; + } + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.spec.js new file mode 100644 index 00000000..8416d1a5 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/create.action.service.spec.js @@ -0,0 +1,65 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 Create l7rule Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'listener1' } }); + } + } + }; + } + }); + $provide.value('$routeParams', {}); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.l7rules.actions.create'); + })); + + it('should not allow creating a l7rule if listenerId is not present', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var allowed = service.allowed(); + permissionShouldFail(allowed); + }); + + it('should handle the action result properly', function() { + var result = service.handle({data: {id: 1}}); + expect(result.created[0].id).toBe(1); + }); + + function permissionShouldFail(permissions) { + permissions.then( + function() { + expect(false).toBe(true); + }, + function() { + expect(true).toBe(true); + }); + } + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.js new file mode 100644 index 00000000..ca7ee7e9 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.js @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules') + .controller('CreateL7RuleWizardController', CreateL7RuleWizardController); + + CreateL7RuleWizardController.$inject = [ + '$scope', + '$routeParams', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + function CreateL7RuleWizardController($scope, $routeParams, model, workflowService, gettext) { + var loadbalancerId = $routeParams.loadbalancerId; + var l7policyId = $routeParams.l7policyId; + var scope = $scope; + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService( + gettext('Create L7 Rule'), + 'fa fa-cloud-download', + ['l7rule'] + ); + scope.model.initialize('l7rule', false, loadbalancerId, l7policyId); + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.spec.js new file mode 100644 index 00000000..700a1c50 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/create/wizard.controller.spec.js @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Create L7Rule Wizard Controller', function() { + var ctrl; + var model = { + submit: function() { + return 'created'; + }, + initialize: angular.noop + }; + var workflow = function() { + return 'foo'; + }; + var scope = { + launchContext: {id: '1234'} + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflow); + })); + beforeEach(inject(function ($controller) { + spyOn(model, 'initialize'); + ctrl = $controller('CreateL7RuleWizardController', { $scope: scope }); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalled(); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe('foo'); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBeDefined(); + expect(scope.submit()).toBe('created'); + }); + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.js new file mode 100644 index 00000000..54db1b10 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.js @@ -0,0 +1,137 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules') + .factory('horizon.dashboard.project.lbaasv2.l7rules.actions.delete', deleteService); + + deleteService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7rules.resourceType', + 'horizon.framework.util.actions.action-result.service', + '$location', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.app.core.openstack-service-api.lbaasv2', + 'horizon.framework.util.i18n.gettext', + 'horizon.app.core.openstack-service-api.policy' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7rules.actions.deleteService + * + * @description + * Brings up the delete l7rule confirmation modal dialog. + * On submit, deletes selected l7rule. + * On cancel, does nothing. + * + * @param resourceType The l7rule resource type. + * @param actionResultService The horizon action result service. + * @param $location The angular $location service. + * @param deleteModal The horizon delete modal service. + * @param api The LBaaS v2 API service. + * @param gettext The horizon gettext function for translation. + * @param policy The horizon policy service. + * + * @returns The l7rule delete service. + */ + + function deleteService( + resourceType, actionResultService, $location, + deleteModal, api, gettext, policy + ) { + var loadbalancerId, listenerId, l7policyId; + + var service = { + perform: perform, + allowed: allowed, + deleteResult: deleteResult // exposed just for testing + }; + + return service; + + ////////////// + + function allowed(/*item*/) { + // This rule is made up and should therefore always pass. I assume at some point there + // will be a valid rule similar to this that we will want to use. + return policy.ifAllowed({ rules: [['neutron', 'delete_l7rule']] }); + } + + function perform(items, scope) { + var context = { }; + var l7rules = angular.isArray(items) ? items : [items]; + context.labels = labelize(l7rules.length); + context.deleteEntity = deleteItem; + l7rules.map(function(item) { + loadbalancerId = item.loadbalancerId; + listenerId = item.listenerId; + l7policyId = item.l7policyId; + }); + return deleteModal.open(scope, l7rules, context).then(deleteResult); + } + + function labelize(count) { + return { + title: ngettext( + 'Confirm Delete L7 Rule', + 'Confirm Delete L7 Rules', count), + + message: ngettext( + 'You have selected "%s". Deleted L7 Rule is not recoverable.', + 'You have selected "%s". Deleted L7 Rules are not recoverable.', count), + + submit: ngettext( + 'Delete L7 Rule', + 'Delete L7 Rules', count), + + success: ngettext( + 'Deleted L7 Rule: %s.', + 'Deleted L7 Rules: %s.', count), + + error: ngettext( + 'Unable to delete L7 Rule: %s.', + 'Unable to delete L7 Rules: %s.', count) + }; + } + + function deleteResult(deleteModalResult) { + // To make the result of this action generically useful, reformat the return + // from the deleteModal into a standard form + var actionResult = actionResultService.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + actionResult.deleted(resourceType, item.context.id); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + actionResult.failed(resourceType, item.context.id); + }); + + if (actionResult.result.failed.length === 0 && actionResult.result.deleted.length > 0) { + var path = 'project/load_balancer/' + loadbalancerId + + '/listeners/' + listenerId + + '/l7policies/' + l7policyId; + $location.path(path); + } + return actionResult.result; + } + + function deleteItem(id) { + return api.deleteL7Rule(l7policyId, id, true); + } + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.spec.js new file mode 100644 index 00000000..5aae2df8 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/delete/delete.action.service.spec.js @@ -0,0 +1,103 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Rule Delete Service', function() { + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module('horizon.framework')); + + var deleteModalService, service, lbaasv2API, policyAPI, $location; + + beforeEach(inject(function($injector) { + service = $injector.get('horizon.dashboard.project.lbaasv2.l7rules.actions.delete'); + lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + deleteModalService = $injector.get('horizon.framework.widgets.modal.deleteModalService'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); + })); + + describe('perform method', function() { + beforeEach(function () { + // just need for this to return something that looks like a promise but does nothing + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + }); + + it('should open the modal with correct label', function () { + service.perform({name: 'spam'}); + var labels = deleteModalService.open.calls.argsFor(0)[2].labels; + expect(deleteModalService.open).toHaveBeenCalled(); + angular.forEach(labels, function eachLabel(label) { + expect(label.toLowerCase()).toContain('l7 rule'); + }); + }); + + it('should open the delete modal with correct entities', function () { + service.perform([{name: 'one'}, {name: 'two'}]); + var entities = deleteModalService.open.calls.argsFor(0)[1]; + expect(deleteModalService.open).toHaveBeenCalled(); + expect(entities.length).toEqual(2); + }); + + it('should pass in a function that deletes a l7 rule', function () { + spyOn(lbaasv2API, 'deleteL7Rule').and.callFake(angular.noop); + service.perform({l7policyId: 2, id: 1, name: 'one'}); + var contextArg = deleteModalService.open.calls.argsFor(0)[2]; + var deleteFunction = contextArg.deleteEntity; + deleteFunction(1); + expect(lbaasv2API.deleteL7Rule).toHaveBeenCalledWith(2, 1, true); + }); + }); + + it('should handle the action result properly', function() { + spyOn($location, 'path'); + spyOn(deleteModalService, 'open').and.returnValue({then: angular.noop}); + spyOn(lbaasv2API, 'deleteL7Rule').and.callFake(angular.noop); + service.perform({loadbalancerId: 1, listenerId: 2, l7policyId: 3, id: 1, name: 'one'}); + var result = service.deleteResult({ + fail: [], + pass: [{ + context: { + id: 1 + } + }] + }); + var path = 'project/load_balancer/1/listeners/2/l7policies/3'; + expect($location.path).toHaveBeenCalledWith(path); + expect(result.deleted[0].id).toBe(1); + result = service.deleteResult({ + pass: [], + fail: [{ + context: { + id: 1 + } + }] + }); + expect(result.failed[0].id).toBe(1); + }); + + describe('allow method', function() { + it('should use default policy if batch action', function () { + spyOn(policyAPI, 'ifAllowed'); + service.allowed(); + expect(policyAPI.ifAllowed).toHaveBeenCalled(); + }); + }); // end of allowed + + }); // end of delete + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.js new file mode 100644 index 00000000..9daced27 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.js @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules') + .factory('horizon.dashboard.project.lbaasv2.l7rules.actions.edit', editService); + + editService.$inject = [ + 'horizon.dashboard.project.lbaasv2.l7rules.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.l7rules.actions.editService + * + * @description + * Provides the service for editing a l7rule resource. + * + * @param resourceType The l7rule resource type. + * @param actionResultService The horizon action result service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param gettext The horizon gettext function for translation. + * + * @returns The l7rule edit service. + */ + + function editService( + resourceType, actionResultService, workflowModal, policy, gettext + ) { + + return workflowModal.init({ + controller: 'EditL7RuleWizardController', + message: gettext('The l7rule has been updated.'), + handle: handle, + allowed: allowed + }); + + function allowed(/*item*/) { + return policy.ifAllowed({ rules: [['neutron', 'update_l7rule']] }); + } + + function handle(response) { + return actionResultService.getActionResult() + .updated(resourceType, response.config.data.l7rule.id) + .result; + } + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.spec.js new file mode 100644 index 00000000..922bc3d2 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/edit.action.service.spec.js @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 Edit L7Rule Action Service', function() { + var policy, service; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$modal', { + open: function() { + return { + result: { + then: function(func) { + func({ data: { id: 'l7rule1' } }); + } + } + }; + } + }); + })); + + beforeEach(inject(function ($injector) { + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + service = $injector.get('horizon.dashboard.project.lbaasv2.l7rules.actions.edit'); + })); + + it('should check policy to allow editing a l7rule', function() { + spyOn(policy, 'ifAllowed').and.returnValue(true); + expect(service.allowed()).toBe(true); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_l7rule']]}); + }); + + it('should handle the action result properly', function() { + var result = service.handle({config: {data: {l7rule: {id: 1}}}}); + expect(result.updated[0].id).toBe(1); + }); + + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.js new file mode 100644 index 00000000..ef98331d --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.js @@ -0,0 +1,60 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.loadbalancers') + .controller('EditL7RuleWizardController', EditL7RuleWizardController); + + EditL7RuleWizardController.$inject = [ + '$scope', + '$routeParams', + '$q', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc controller + * @name EditL7RuleWizardController + * + * @description + * Controller for the LBaaS v2 edit l7rule wizard. + * + * @param $scope The angular scope object. + * @param $routeParams The angular $routeParams service. + * @param $q The angular service for promises. + * @param model The LBaaS V2 workflow model service. + * @param workflowService The LBaaS V2 workflow service. + * @param gettext The horizon gettext function for translation. + * @returns undefined + */ + + function EditL7RuleWizardController($scope, $routeParams, $q, model, workflowService, gettext) { + var scope = $scope; + var loadbalancerId = $routeParams.loadbalancerId; + var l7policyId = $routeParams.l7policyId; + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService( + gettext('Update L7 Rule'), + 'fa fa-pencil', ['l7rule']); + scope.model.initialize('l7rule', scope.launchContext.id, loadbalancerId, l7policyId); + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.spec.js new file mode 100644 index 00000000..dc04135d --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/actions/edit/wizard.controller.spec.js @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Edit L7Rule Wizard Controller', function() { + var ctrl, workflowSpy, $q, scope; + var model = { + submit: function() { + return 'updated'; + }, + initialize: function() { + var defer = $q.defer(); + defer.resolve(); + return defer.promise; + } + }; + var workflow = { + steps: [{id: 'l7rule'}], + append: angular.noop + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + workflowSpy = jasmine.createSpy('workflow').and.returnValue(workflow); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflowSpy); + })); + beforeEach(inject(function ($controller, $injector) { + $q = $injector.get('$q'); + scope = $injector.get('$rootScope').$new(); + scope.launchContext = { id: 'l7ruleId' }; + spyOn(model, 'initialize').and.callThrough(); + ctrl = $controller('EditL7RuleWizardController', { + $scope: scope, + $routeParams: {loadbalancerId: 'loadbalancerId', l7policyId: 'l7policyId'}}); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalledWith( + 'l7rule', 'l7ruleId', 'loadbalancerId', 'l7policyId'); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe(workflow); + }); + + it('initializes workflow with correct properties', function() { + expect(workflowSpy).toHaveBeenCalledWith('Update L7 Rule', + 'fa fa-pencil', ['l7rule']); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBe(model.submit); + expect(scope.submit()).toBe('updated'); + }); + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.js new file mode 100644 index 00000000..d1f4e09f --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.js @@ -0,0 +1,119 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules') + .controller('L7RuleDetailController', L7RuleDetailController); + + L7RuleDetailController.$inject = [ + 'loadbalancer', + 'listener', + 'l7policy', + 'l7rule', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.l7rules.resourceType', + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.framework.widgets.modal-wait-spinner.service', + '$q' + ]; + + /** + * @ngdoc controller + * @name L7RuleDetailController + * + * @description + * Controller for the LBaaS v2 l7rule detail page. + * + * @param loadbalancer The loadbalancer object. + * @param listener The listener object. + * @param l7policy The l7policy object. + * @param l7rule The l7rule object. + * @param loadBalancersService The LBaaS v2 load balancers service. + * @param resourceType The load balancer resource type. + * @param typeRegistry The horizon type registry service. + * @param spinnerService The horizon modal wait spinner service. + * @param $q The angular service for promises. + * + * @returns undefined + */ + + function L7RuleDetailController( + loadbalancer, listener, l7policy, l7rule, loadBalancersService, + resourceType, typeRegistry, spinnerService, $q + ) { + var ctrl = this; + + ctrl.operatingStatus = loadBalancersService.operatingStatus; + ctrl.provisioningStatus = loadBalancersService.provisioningStatus; + ctrl.l7ruleType = loadBalancersService.l7ruleType; + ctrl.l7ruleCompareType = loadBalancersService.l7ruleCompareType; + ctrl.loadbalancer = loadbalancer; + ctrl.listener = listener; + ctrl.l7policy = l7policy; + ctrl.l7rule = l7rule; + ctrl.listFunctionExtraParams = { + loadbalancerId: ctrl.loadbalancer.id, + listenerId: ctrl.listener.id, + l7policyId: ctrl.l7policy.id, + l7ruleId: ctrl.l7rule.id + }; + ctrl.resourceType = typeRegistry.getResourceType(resourceType); + ctrl.context = {}; + ctrl.context.l7policyId = l7policy.id; + ctrl.context.l7ruleId = l7rule.id; + + ctrl.resultHandler = actionResultHandler; + + function actionResultHandler(returnValue) { + return $q.when(returnValue, actionSuccessHandler); + } + + function loadData(response) { + spinnerService.hideModalSpinner(); + ctrl.showDetails = true; + ctrl.resourceType.initActions(); + ctrl.l7rule = response.data; + ctrl.l7rule.loadbalancerId = ctrl.loadbalancer.id; + ctrl.l7rule.listenerId = ctrl.listener.id; + ctrl.l7rule.l7policyId = ctrl.l7policy.id; + } + + function actionSuccessHandler(result) { + // The action has completed (for whatever "complete" means to that + // action. Notice the view doesn't really need to know the semantics of the + // particular action because the actions return data in a standard form. + // That return includes the id and type of each created, updated, deleted + // and failed item. + // Currently just refreshes the display each time. + if (result) { + if (result.failed.length === 0 && result.deleted.length > 0) { + // handle a race condition where the resource is already deleted + return; + } + spinnerService.showModalSpinner(gettext('Please Wait')); + ctrl.showDetails = false; + ctrl.context.loadPromise = ctrl.resourceType.load( + ctrl.context.l7policyId, + ctrl.context.l7ruleId + ); + ctrl.context.loadPromise.then(loadData); + } + } + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.spec.js new file mode 100644 index 00000000..e37e256c --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.controller.spec.js @@ -0,0 +1,114 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Rule Detail Controller', function() { + var deferred, service, ctrl, scope, $timeout, $q, actionResultService; + + /////////////////////// + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + $provide.value('$uibModal', {}); + })); + + beforeEach(inject(function($controller, $rootScope, _$q_, _$timeout_) { + $q = _$q_; + deferred = $q.defer(); + service = { + getResourceType: function() { + return { + load: function() { return deferred.promise; }, + parsePath: function() { return 'my-context'; }, + itemName: function() { return 'A name'; }, + initActions: angular.noop + }; + }, + getDefaultDetailsTemplateUrl: angular.noop + }; + actionResultService = { + getIdsOfType: function() { return []; } + }; + $timeout = _$timeout_; + scope = $rootScope.$new(); + ctrl = $controller('L7RuleDetailController', { + $scope: scope, + loadbalancer: { id: '123' }, + listener: { id: '123' }, + l7policy: { id: '123' }, + l7rule: { id: '123' }, + 'horizon.framework.conf.resource-type-registry.service': service, + 'horizon.framework.util.actions.action-result.service': actionResultService, + 'horizon.framework.widgets.modal-wait-spinner.service': { + showModalSpinner: angular.noop, + hideModalSpinner: angular.noop + } + }); + })); + + it('should create a controller', function() { + expect(ctrl).toBeDefined(); + expect(ctrl.loadbalancer).toBeDefined(); + expect(ctrl.listener).toBeDefined(); + expect(ctrl.l7policy).toBeDefined(); + expect(ctrl.l7rule).toBeDefined(); + }); + + describe('resultHandler', function() { + + it('handles empty results', function() { + var result = $q.defer(); + result.resolve({failed: [], deleted: []}); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles falsy results', function() { + var result = $q.defer(); + result.resolve(false); + ctrl.resultHandler(result.promise); + $timeout.flush(); + expect(ctrl.showDetails).not.toBe(true); + }); + + it('handles matched results', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: []}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(true); + }); + + it('handles delete race condition', function() { + spyOn(actionResultService, 'getIdsOfType').and.returnValue([1, 2, 3]); + var result = $q.defer(); + result.resolve({some: 'thing', failed: [], deleted: [{id: 1}]}); + ctrl.resultHandler(result.promise); + deferred.resolve({data: {some: 'data'}}); + $timeout.flush(); + expect(ctrl.showDetails).toBe(undefined); + }); + + }); + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.html new file mode 100644 index 00000000..97368df7 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/details/detail.html @@ -0,0 +1,54 @@ + +
+
+ + +
+ + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.js new file mode 100644 index 00000000..3a512cbe --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.js @@ -0,0 +1,181 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.dashboard.project.lbaasv2.l7rules + * + * @description + * Provides the services and widgets required to support and display the project l7 rules + * for the load balancers v2 panel. + */ + + angular + .module('horizon.dashboard.project.lbaasv2.l7rules', []) + .constant('horizon.dashboard.project.lbaasv2.l7rules.resourceType', + 'OS::Octavia::L7Rule') + .run(run); + + run.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.project.lbaasv2.basePath', + 'horizon.dashboard.project.lbaasv2.loadbalancers.service', + 'horizon.dashboard.project.lbaasv2.l7rules.actions.create', + 'horizon.dashboard.project.lbaasv2.l7rules.actions.edit', + 'horizon.dashboard.project.lbaasv2.l7rules.actions.delete', + 'horizon.dashboard.project.lbaasv2.l7rules.resourceType' + ]; + + function run( + registry, + basePath, + loadBalancerService, + createService, + editService, + deleteService, + resourceType + ) { + var l7ruleResourceType = registry.getResourceType(resourceType); + + l7ruleResourceType + .setNames(gettext('L7 Rule'), gettext('L7 Rules')) + .setSummaryTemplateUrl(basePath + 'l7rules/details/drawer.html') + .setProperties(l7ruleProperties(loadBalancerService)) + .setListFunction(loadBalancerService.getL7RulesPromise) + .setLoadFunction(loadBalancerService.getL7RulePromise) + .tableColumns + .append({ + id: 'type', + priority: 1, + urlFunction: loadBalancerService.getL7RuleDetailsPath + }) + .append({ + id: 'compare_type', + priority: 1 + }) + .append({ + id: 'key', + priority: 1 + }) + .append({ + id: 'rule_value', + priority: 1 + }) + .append({ + id: 'invert', + priority: 1 + }) + .append({ + id: 'operating_status', + priority: 1 + }) + .append({ + id: 'provisioning_status', + priority: 1 + }) + .append({ + id: 'admin_state_up', + priority: 1 + }); + + l7ruleResourceType.itemActions + .append({ + id: 'l7ruleEdit', + service: editService, + template: { + text: gettext('Edit L7 Rule') + } + }) + .append({ + id: 'l7ruleDelete', + service: deleteService, + template: { + text: gettext('Delete L7 Rule'), + type: 'delete' + } + }); + + l7ruleResourceType.globalActions + .append({ + id: 'l7ruleCreate', + service: createService, + template: { + type: 'create', + text: gettext('Create L7 Rule') + } + }); + + l7ruleResourceType.batchActions + .append({ + id: 'l7ruleBatchDelete', + service: deleteService, + template: { + text: gettext('Delete L7 Rules'), + type: 'delete-selected' + } + }); + } + + function l7ruleProperties(loadBalancerService) { + return { + id: gettext('ID'), + type: { + label: gettext('Type'), + values: loadBalancerService.l7ruleType + }, + compare_type: { + label: gettext('Compare Type'), + values: loadBalancerService.l7ruleCompareType + }, + provisioning_status: { + label: gettext('Provisioning Status'), + values: loadBalancerService.provisioningStatus + }, + operating_status: { + label: gettext('Operating Status'), + values: loadBalancerService.operatingStatus + }, + admin_state_up: { + label: gettext('Admin State Up'), + filters: ['yesno'] + }, + key: { + label: gettext('Key'), + filters: ['noName'] + }, + rule_value: { + label: gettext('Value'), + filters: ['noName'] + }, + invert: { + label: gettext('Invert'), + filters: ['yesno'] + }, + project_id: gettext('Project ID'), + created_at: { + label: gettext('Created At'), + filters: ['noValue'] + }, + updated_at: { + label: gettext('Updated At'), + filters: ['noValue'] + } + }; + } + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.spec.js new file mode 100644 index 00000000..6fc42122 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/l7rules/l7rules.module.spec.js @@ -0,0 +1,67 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 L7Rules Module', function() { + it('should exist', function() { + expect(angular.module('horizon.dashboard.project.lbaasv2.l7rules')).toBeDefined(); + }); + }); + + describe('LBaaS v2 L7Rules Registry', function () { + var registry, resourceType; + + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(inject(function($injector) { + resourceType = $injector.get('horizon.dashboard.project.lbaasv2.l7rules.resourceType'); + registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); + })); + + it('should define resourceType', function () { + expect(resourceType).toBeDefined(); + }); + + it('should register item actions', function () { + var actions = registry.getResourceType(resourceType).itemActions; + expect(actionHasId(actions, 'l7ruleEdit')).toBe(true); + expect(actionHasId(actions, 'l7ruleDelete')).toBe(true); + }); + + it('should register global actions', function () { + var actions = registry.getResourceType(resourceType).globalActions; + expect(actionHasId(actions, 'l7ruleCreate')).toBe(true); + }); + + it('should register batch actions', function () { + var actions = registry.getResourceType(resourceType).batchActions; + expect(actionHasId(actions, 'l7ruleBatchDelete')).toBe(true); + }); + + function actionHasId(list, value) { + return list.filter(matchesId).length === 1; + + function matchesId(action) { + if (action.id === value) { + return true; + } + } + } + + }); + +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js index f1f8d482..e10aa289 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.js @@ -28,6 +28,8 @@ .module('horizon.dashboard.project.lbaasv2', [ 'horizon.dashboard.project.lbaasv2.loadbalancers', 'horizon.dashboard.project.lbaasv2.listeners', + 'horizon.dashboard.project.lbaasv2.l7policies', + 'horizon.dashboard.project.lbaasv2.l7rules', 'horizon.dashboard.project.lbaasv2.pools', 'horizon.dashboard.project.lbaasv2.members', 'horizon.dashboard.project.lbaasv2.healthmonitors', @@ -68,6 +70,8 @@ var loadbalancers = '/project/load_balancer'; var listener = loadbalancers + '/:loadbalancerId/listeners/:listenerId'; + var listenerL7Policy = listener + '/l7policies/:l7policyId'; + var listenerL7Rule = listenerL7Policy + '/l7rules/:l7ruleId'; var listenerPool = listener + '/pools/:poolId'; var listenerPoolMember = listenerPool + '/members/:memberId'; var listenerPoolHealthmonitor = listenerPool + '/healthmonitors/:healthmonitorId'; @@ -129,6 +133,105 @@ controller: 'ListenerDetailController', controllerAs: 'ctrl' }) + .when(listenerL7Policy, { + templateUrl: basePath + 'l7policies/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + return response.data; + } + ); + } + ], + l7policy: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getL7Policy($route.current.params.l7policyId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + response.data.listenerId = $route.current.params.listenerId; + return response.data; + } + ); + } + ] + }, + controller: 'L7PolicyDetailController', + controllerAs: 'ctrl' + }) + .when(listenerL7Rule, { + templateUrl: basePath + 'l7rules/details/detail.html', + resolve: { + loadbalancer: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getLoadBalancer($route.current.params.loadbalancerId, true).then( + function success(response) { + response.data.floating_ip_address = response.data.floating_ip.ip; + return response.data; + } + ); + } + ], + listener: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getListener($route.current.params.listenerId).then( + function success(response) { + return response.data; + } + ); + } + ], + l7policy: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getL7Policy($route.current.params.l7policyId).then( + function success(response) { + return response.data; + } + ); + } + ], + l7rule: [ + '$route', + 'horizon.app.core.openstack-service-api.lbaasv2', + function($route, api) { + return api.getL7Rule($route.current.params.l7policyId, + $route.current.params.l7ruleId).then( + function success(response) { + response.data.loadbalancerId = $route.current.params.loadbalancerId; + response.data.listenerId = $route.current.params.listenerId; + response.data.l7policyId = $route.current.params.l7policyId; + return response.data; + } + ); + } + ] + }, + controller: 'L7RuleDetailController', + controllerAs: 'ctrl' + }) .when(listenerPool, { templateUrl: basePath + 'pools/details/detail.html', resolve: { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js index 174ec00c..45e53f16 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/lbaasv2.module.spec.js @@ -233,6 +233,121 @@ }); })); + it('should route resolved listener l7 policy detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function l7policyAPI() { + var l7policy = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(l7policy); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + spyOn(lbaasv2API, 'getL7Policy').and.callFake(l7policyAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/l7policies/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2/l7policies/3'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + + it('should route resolved listener l7 rule detail', inject(function($injector) { + function loadbalancerAPI() { + var loadbalancer = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(loadbalancer); + }, + then: function(callback) { + callback({ data: { id: 1, floating_ip: {}}}); + } + }; + } + + function listenerAPI() { + var listener = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(listener); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function l7policyAPI() { + var l7policy = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(l7policy); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + function l7ruleAPI() { + var l7rule = { provisioning_status: 'ACTIVE' }; + return { + success: function(callback) { + callback(l7rule); + }, + then: function(callback) { + callback({ data: { id: 1}}); + } + }; + } + + var lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); + spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); + spyOn(lbaasv2API, 'getListener').and.callFake(listenerAPI); + spyOn(lbaasv2API, 'getL7Policy').and.callFake(l7policyAPI); + spyOn(lbaasv2API, 'getL7Rule').and.callFake(l7ruleAPI); + inject(function($route, $location, $rootScope, $httpBackend) { + $httpBackend.expectGET( + '/static/dashboard/project/lbaasv2/l7rules/details/detail.html' + ).respond({}); + $location.path('/project/load_balancer/1/listeners/2/l7policies/3/l7rules/4'); + $rootScope.$digest(); + expect($route.current).toBeDefined(); + }); + })); + it('should route resolved listener pool member detail', inject(function($injector) { function loadbalancerAPI() { var loadbalancer = { provisioning_status: 'ACTIVE' }; diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html index 7a4a2ce7..f810dde1 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/listeners/details/detail.html @@ -61,4 +61,10 @@ list-function-extra-params="ctrl.listFunctionExtraParams"> + + + + diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.js index 3a99c6b0..daf16765 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/create/wizard.controller.js @@ -33,7 +33,11 @@ // the wizard continues to work. Using local var to appease eslint angular/ng_controller_as. scope.model = model; scope.submit = scope.model.submit; - scope.workflow = workflowService(gettext('Create Load Balancer'), 'fa fa-cloud-download'); + scope.workflow = workflowService( + gettext('Create Load Balancer'), + 'fa fa-cloud-download', + ['loadbalancer', 'listener', 'certificates', 'pool', 'members', 'monitor'] + ); scope.model.initialize('loadbalancer'); } diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js index 54fdf3db..1302e1d1 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.js @@ -64,6 +64,28 @@ SOURCE_IP: gettext('Source IP') }; + var l7policyAction = { + REJECT: gettext('Reject'), + REDIRECT_TO_URL: gettext('Redirect to URL'), + REDIRECT_TO_POOL: gettext('Redirect to Pool') + }; + + var l7ruleType = { + HOST_NAME: gettext('Host Name'), + PATH: gettext('Path'), + FILE_TYPE: gettext('File Type'), + HEADER: gettext('Header'), + COOKIE: gettext('Cookie') + }; + + var l7ruleCompareType = { + REGEX: gettext('Regex'), + STARTS_WITH: gettext('Starts With'), + ENDS_WITH: gettext('Ends With'), + CONTAINS: gettext('Contains'), + EQUAL_TO: gettext('Equal To') + }; + var none = { null: gettext('None') }; @@ -72,6 +94,9 @@ operatingStatus: operatingStatus, provisioningStatus: provisioningStatus, loadBalancerAlgorithm: loadBalancerAlgorithm, + l7policyAction: l7policyAction, + l7ruleType: l7ruleType, + l7ruleCompareType: l7ruleCompareType, none: none, nullFilter: nullFilter, getLoadBalancersPromise: getLoadBalancersPromise, @@ -80,6 +105,12 @@ getListenersPromise: getListenersPromise, getListenerPromise: getListenerPromise, getListenerDetailsPath: getListenerDetailsPath, + getL7PoliciesPromise: getL7PoliciesPromise, + getL7PolicyPromise: getL7PolicyPromise, + getL7PolicyDetailsPath: getL7PolicyDetailsPath, + getL7RulesPromise: getL7RulesPromise, + getL7RulePromise: getL7RulePromise, + getL7RuleDetailsPath: getL7RuleDetailsPath, getPoolsPromise: getPoolsPromise, getPoolPromise: getPoolPromise, getPoolDetailsPath: getPoolDetailsPath, @@ -200,6 +231,58 @@ } } + function getL7RulesPromise(params) { + return api.getL7Rules(params.l7policyId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + item.listenerId = params.listenerId; + item.l7policyId = params.l7policyId; + return item; + } + } + } + + function getL7RulePromise(l7policyId, l7ruleId) { + return api.getL7Rule(l7policyId, l7ruleId); + } + + function getL7RuleDetailsPath(item) { + return 'project/load_balancer/' + + item.loadbalancerId + '/listeners/' + + item.listenerId + '/l7policies/' + + item.l7policyId + '/l7rules/' + item.id; + } + + function getL7PoliciesPromise(params) { + return api.getL7Policies(params.listenerId).then(modifyResponse); + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + item.trackBy = item.id + item.updated_at; + item.loadbalancerId = params.loadbalancerId; + item.listenerId = params.listenerId; + return item; + } + } + } + + function getL7PolicyPromise(identifier) { + return api.getL7Policy(identifier); + } + + function getL7PolicyDetailsPath(item) { + return 'project/load_balancer/' + + item.loadbalancerId + '/listeners/' + + item.listenerId + '/l7policies/' + item.id; + } + function getListenersPromise(params) { return api.getListeners(params.loadbalancerId).then(modifyResponse); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js index e5222821..4f05316d 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/loadbalancers/loadbalancers.service.spec.js @@ -91,6 +91,61 @@ expect(result.$$state.value.data.updated_at).toBe('feb8'); })); + it('getL7PolicyDetailsPath creates urls using the item\'s ID', function() { + var myItem = {loadbalancerId: '123', id: '789', listenerId: '456'}; + expect(service.getL7PolicyDetailsPath(myItem)) + .toBe('project/load_balancer/123/listeners/456/l7policies/789'); + }); + + it("getL7PoliciesPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getL7Policies').and.returnValue(deferred.promise); + var result = service.getL7PoliciesPromise({listenerId: 3}); + deferred.resolve({data: {items: [{id: 1, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(1); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('1feb8'); + expect(result.$$state.value.data.items[0].listenerId).toBe(3); + })); + + it("getL7PolicyPromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getL7Policy').and.returnValue(deferred.promise); + var result = service.getL7PolicyPromise(1); + deferred.resolve({data: {id: 1, updated_at: 'feb8'}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + + it('getL7RuleDetailsPath creates urls using the item\'s ID', function() { + var myItem = {loadbalancerId: '1', id: '5', listenerId: '2', l7policyId: '3'}; + expect(service.getL7RuleDetailsPath(myItem)) + .toBe('project/load_balancer/1/listeners/2/l7policies/3/l7rules/5'); + }); + + it("getL7RulesPromise provides a promise", inject(function($timeout) { + var deferred = $q.defer(); + spyOn(api, 'getL7Rules').and.returnValue(deferred.promise); + var result = service.getL7RulesPromise({listenerId: 3, l7policyId: 5}); + deferred.resolve({data: {items: [{id: 1, updated_at: 'feb8'}]}}); + $timeout.flush(); + expect(result.$$state.value.data.items[0].id).toBe(1); + expect(result.$$state.value.data.items[0].updated_at).toBe('feb8'); + expect(result.$$state.value.data.items[0].trackBy).toBe('1feb8'); + expect(result.$$state.value.data.items[0].listenerId).toBe(3); + expect(result.$$state.value.data.items[0].l7policyId).toBe(5); + })); + + it("getL7RulePromise provides a promise", inject(function() { + var deferred = $q.defer(); + spyOn(api, 'getL7Rule').and.returnValue(deferred.promise); + var result = service.getL7RulePromise(2, 1); + deferred.resolve({data: {id: 1, updated_at: 'feb8'}}); + expect(result.$$state.value.data.id).toBe(1); + expect(result.$$state.value.data.updated_at).toBe('feb8'); + })); + it('getPoolDetailsPath creates urls using the item\'s ID', function() { var myItem = {loadbalancerId: '123', id: '789', listeners: [{id: '456'}]}; expect(service.getPoolDetailsPath(myItem)) diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.js new file mode 100644 index 00000000..6a552dd9 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.js @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2') + .controller('L7PolicyDetailsController', L7PolicyDetailsController); + + L7PolicyDetailsController.$inject = [ + 'horizon.dashboard.project.lbaasv2.patterns', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc controller + * @name L7PolicyDetailsController + * @description + * The `L7PolicyDetailsController` controller provides functions for + * configuring the l7policy step of the LBaaS wizard. + * @param patterns The LBaaS v2 patterns constant. + * @param gettext The horizon gettext function for translation. + * @returns undefined + */ + + function L7PolicyDetailsController(patterns, gettext) { + + var ctrl = this; + + ctrl.adminStateUpOptions = [ + { label: gettext('Yes'), value: true }, + { label: gettext('No'), value: false } + ]; + + ctrl.redirectUrlError = gettext('The redirect url must be a valid http or https url.'); + ctrl.positionError = gettext('The position must be a number between 1 and 2147483647.'); + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.spec.js new file mode 100644 index 00000000..7c4dfb21 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.controller.spec.js @@ -0,0 +1,37 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('L7Policy Details Step', function() { + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + describe('L7PolicyDetailsController', function() { + var ctrl; + + beforeEach(inject(function($controller) { + ctrl = $controller('L7PolicyDetailsController'); + })); + + it('should define adminStateUpOptions', function() { + expect(ctrl.adminStateUpOptions).toBeDefined(); + }); + + }); + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.help.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.help.html new file mode 100644 index 00000000..4b1459c5 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.help.html @@ -0,0 +1,38 @@ +

+ An L7 Policy is a collection of L7 rules associated with a Listener, and which may also have an association to a back-end pool +

+

+ Action: + + The L7 policy action. One of REJECT, REDIRECT_TO_URL, or REDIRECT_TO_POOL. + +

    +
  • + REJECT: The request is denied with an appropriate response code, and not forwarded on to any back-end pool. +
  • +
  • + REDIRECT_TO_URL: The request is sent an HTTP redirect to the URL defined in the redirect_url parameter. +
  • +
  • + REDIRECT_TO_POOL: The request is forwarded to the back-end pool associated with the L7 policy. +
  • +
+

+

+ Position: + + The position of this policy on the listener. Positions start at 1. + +

+

+ Redirect Pool ID: + + Requests matching this policy will be redirected to the pool with this ID. Only valid if action is REDIRECT_TO_POOL. + +

+

+ Redirect URL: + + Requests matching this policy will be redirected to this URL. Only valid if action is REDIRECT_TO_URL. + +

diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.html new file mode 100644 index 00000000..a9962bd7 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7policy/l7policy.html @@ -0,0 +1,104 @@ +
+

Provide the details for the l7 policy.

+ +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ + +
+
+ +
+
+ + + + {$ ::ctrl.redirectUrlError $} + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ + + + {$ ::ctrl.positionError $} + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.js new file mode 100644 index 00000000..d98971e7 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.js @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2') + .controller('L7RuleDetailsController', L7RuleDetailsController); + + L7RuleDetailsController.$inject = [ + 'horizon.dashboard.project.lbaasv2.patterns', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc controller + * @name L7RuleDetailsController + * @description + * The `L7RuleDetailsController` controller provides functions for + * configuring the l7rule step of the LBaaS wizard. + * @param patterns The LBaaS v2 patterns constant. + * @param gettext The horizon gettext function for translation. + * @returns undefined + */ + + function L7RuleDetailsController(patterns, gettext) { + + var ctrl = this; + + ctrl.adminStateUpOptions = [ + { label: gettext('Yes'), value: true }, + { label: gettext('No'), value: false } + ]; + + } +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.spec.js new file mode 100644 index 00000000..9eb3a6ff --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.controller.spec.js @@ -0,0 +1,37 @@ +/* + * Copyright 2018 Walmart. + * + * 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. + */ +(function() { + 'use strict'; + + describe('L7Rule Details Step', function() { + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + describe('L7RuleDetailsController', function() { + var ctrl; + + beforeEach(inject(function($controller) { + ctrl = $controller('L7RuleDetailsController'); + })); + + it('should define adminStateUpOptions', function() { + expect(ctrl.adminStateUpOptions).toBeDefined(); + }); + + }); + }); +})(); diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.help.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.help.html new file mode 100644 index 00000000..a68f12d1 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.help.html @@ -0,0 +1,76 @@ +

+An L7 Rule is a single, simple logical test which returns either true or +false. +

+

+ Type: + + The L7 rule type. One of COOKIE, FILE_TYPE, HEADER, HOST_NAME, or PATH. + +

    +
  • + COOKIE: The rule looks for a cookie named by the key parameter and + compares it against the value parameter in the rule. +
  • +
  • + HEADER: The rule looks for a header defined in the key parameter + and compares it against the value parameter in the rule. +
  • +
  • + FILE_TYPE: The rule compares the last portion of the URI against + the value parameter in the rule. (eg. “txt”, “jpg”, etc.) +
  • +
  • + PATH: The rule compares the path portion of the HTTP URI against + the value parameter in the rule. +
  • +
  • + HOST_NAME: The rule does a comparison between the HTTP/1.1 + hostname in the request against the value parameter in the rule. +
  • +
+

+

+ Compare Type: + + The comparison type for the L7 rule. One of CONTAINS, ENDS_WITH, + EQUAL_TO, REGEX, or STARTS_WITH. + +

    +
  • + REGEX: Perl type regular expression matching. +
  • +
  • + STARTS_WITH: String starts with. +
  • +
  • + ENDS_WITH: String ends with. +
  • +
  • + CONTAINS: String contains. +
  • +
  • + EQUAL_TO: String is equal to. +
  • +
+

+

+ Key: + + The key to use for the comparison. For example, the name of the cookie + to evaluate. + +

+

+ Value: + + The value to use for the comparison. For example, the file type to compare. + +

+

+ Invert: + + When true the logic of the rule is inverted. For example, with invert + true, equal to would become not equal to. + +

diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.html b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.html new file mode 100644 index 00000000..b5b637f0 --- /dev/null +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/l7rule/l7rule.html @@ -0,0 +1,92 @@ +
+

Provide the details for the l7 rule.

+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js index a329604b..e7533683 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js @@ -86,6 +86,9 @@ subnets: [], members: [], listenerProtocols: ['HTTP', 'TCP', 'TERMINATED_HTTPS', 'HTTPS'], + l7policyActions: ['REJECT', 'REDIRECT_TO_URL', 'REDIRECT_TO_POOL'], + l7ruleTypes: ['HOST_NAME', 'PATH', 'FILE_TYPE', 'HEADER', 'COOKIE'], + l7ruleCompareTypes: ['REGEX', 'STARTS_WITH', 'ENDS_WITH', 'CONTAINS', 'EQUAL_TO'], poolProtocols: ['HTTP', 'HTTPS', 'PROXY', 'TCP'], methods: ['LEAST_CONNECTIONS', 'ROUND_ROBIN', 'SOURCE_IP'], types: ['SOURCE_IP', 'HTTP_COOKIE', 'APP_COOKIE'], @@ -153,6 +156,25 @@ admin_state_up: true, default_pool_id: null }, + l7policy: { + id: null, + name: gettext('L7 Policy 1'), + description: null, + action: null, + position: null, + redirect_pool_id: null, + redirect_url: null, + admin_state_up: true + }, + l7rule: { + id: null, + type: null, + compare_type: null, + key: null, + rule_value: null, + invert: false, + admin_state_up: true + }, pool: { id: null, name: gettext('Pool 1'), @@ -198,11 +220,15 @@ var promise = { createloadbalancer: initCreateLoadBalancer, createlistener: initCreateListener, + createl7policy: initCreateL7Policy, + createl7rule: initCreateL7Rule, createpool: initCreatePool, createmonitor: initCreateMonitor, createmembers: initUpdateMemberList, editloadbalancer: initEditLoadBalancer, editlistener: initEditListener, + editl7policy: initEditL7Policy, + editl7rule: initEditL7Rule, editpool: initEditPool, editmonitor: initEditMonitor }[type](keymanagerPromise); @@ -242,6 +268,17 @@ ]).then(initMemberAddresses); } + function initCreateL7Policy() { + model.context.submit = createL7Policy; + return lbaasv2API.getListener(model.spec.parentResourceId) + .then(onGetListener).then(getPools).then(onGetPools); + } + + function initCreateL7Rule() { + model.context.submit = createL7Rule; + return $q.when(); + } + function initCreatePool() { model.context.submit = createPool; // We get the listener details here because we need to know the listener protocol @@ -295,6 +332,19 @@ ]).then(initMemberAddresses); } + function initEditL7Policy() { + model.context.submit = editL7Policy; + return lbaasv2API + .getListener(model.spec.parentResourceId).then(onGetListener) + .then(getPools).then(onGetPools) + .then(getL7Policy).then(onGetL7Policy); + } + + function initEditL7Rule() { + model.context.submit = editL7Rule; + return getL7Rule().then(onGetL7Rule); + } + function initEditPool() { model.context.submit = editPool; return $q.all([ @@ -339,6 +389,14 @@ return lbaasv2API.createListener(spec); } + function createL7Policy(spec) { + return lbaasv2API.createL7Policy(spec); + } + + function createL7Rule(spec) { + return lbaasv2API.createL7Rule(model.spec.parentResourceId, spec); + } + function createPool(spec) { return lbaasv2API.createPool(spec); } @@ -355,6 +413,14 @@ return lbaasv2API.editListener(model.context.id, spec); } + function editL7Policy(spec) { + return lbaasv2API.editL7Policy(model.context.id, spec); + } + + function editL7Rule(spec) { + return lbaasv2API.editL7Rule(model.spec.parentResourceId, model.context.id, spec); + } + function editPool(spec) { return lbaasv2API.editPool(model.context.id, spec); } @@ -576,6 +642,15 @@ return lbaasv2API.getListener(model.context.id, true); } + function getL7Policy() { + return lbaasv2API.getL7Policy(model.context.id, true); + } + + function getL7Rule() { + return lbaasv2API.getL7Rule(model.spec.parentResourceId, + model.context.id); + } + function getPool() { return lbaasv2API.getPool(model.context.id, true); } @@ -633,6 +708,18 @@ } } + function onGetL7Policy(response) { + var result = response.data; + + setL7PolicySpec(result.l7policy || result); + } + + function onGetL7Rule(response) { + var result = response.data; + + setL7RuleSpec(result.l7rule || result); + } + function onGetPool(response) { var result = response.data; @@ -674,6 +761,29 @@ spec.default_pool_id = listener.default_pool_id; } + function setL7PolicySpec(l7policy) { + var spec = model.spec.l7policy; + spec.id = l7policy.id; + spec.name = l7policy.name; + spec.description = l7policy.description; + spec.action = l7policy.action; + spec.position = l7policy.position; + spec.redirect_pool_id = l7policy.redirect_pool_id; + spec.redirect_url = l7policy.redirect_url; + spec.admin_state_up = l7policy.admin_state_up; + } + + function setL7RuleSpec(l7rule) { + var spec = model.spec.l7rule; + spec.id = l7rule.id; + spec.type = l7rule.type; + spec.compare_type = l7rule.compare_type; + spec.key = l7rule.key; + spec.rule_value = l7rule.rule_value; + spec.invert = l7rule.invert; + spec.admin_state_up = l7rule.admin_state_up; + } + function setPoolSpec(pool) { var spec = model.spec.pool; spec.id = pool.id; diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js index 731679c4..eec254be 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js @@ -141,6 +141,37 @@ deferred.resolve({ data: listenerData }); return deferred.promise; }, + getL7Policy: function() { + var l7policy = { + admin_state_up: true, + id: '1234', + name: 'L7 Policy 1', + description: 'l7 policy description', + action: 'REDIRECT_TO_URL', + position: 1, + redirect_url: 'http://example.com' + }; + + var deferred = $q.defer(); + deferred.resolve({ data: l7policy }); + + return deferred.promise; + }, + getL7Rule: function() { + var l7rule = { + admin_state_up: true, + id: '1234', + type: 'HOST_NAME', + compare_type: 'EQUAL_TO', + value: 'api.example.com', + invert: false + }; + + var deferred = $q.defer(); + deferred.resolve({ data: l7rule }); + + return deferred.promise; + }, getPool: function() { var poolResources = angular.copy(listenerResources); delete poolResources.listener; @@ -202,6 +233,18 @@ editListener: function(id, spec) { return spec; }, + createL7Policy: function(spec) { + return spec; + }, + editL7Policy: function(id, spec) { + return spec; + }, + createL7Rule: function(l7policyId, spec) { + return spec; + }, + editL7Rule: function(l7policyId, l7ruleId, spec) { + return spec; + }, createPool: function(spec) { return spec; }, @@ -463,6 +506,82 @@ }); }); + describe('Post initialize model (create l7 policy)', function() { + + beforeEach(function() { + includeChildResources = false; + model.initialize('l7policy', false, '1234', '5678'); + scope.$apply(); + }); + + it('should initialize model properties', function() { + expect(model.initializing).toBe(false); + expect(model.initialized).toBe(true); + expect(model.subnets.length).toBe(0); + expect(model.members.length).toBe(0); + expect(model.certificates.length).toBe(0); + expect(model.listenerPorts.length).toBe(0); + expect(model.spec).toBeDefined(); + expect(model.spec.loadbalancer_id).toBe('1234'); + expect(model.spec.parentResourceId).toBe('5678'); + expect(model.spec.loadbalancer).toBeDefined(); + expect(model.spec.listener).toBeDefined(); + expect(model.spec.pool).toBeDefined(); + expect(model.spec.members.length).toBe(0); + expect(model.spec.certificates).toEqual([]); + expect(model.spec.monitor).toBeDefined(); + expect(model.certificatesError).toBe(false); + }); + + it('should initialize names', function() { + expect(model.spec.l7policy.name).toBe('L7 Policy 1'); + }); + + it('should initialize context properties', function() { + expect(model.context.resource).toBe('l7policy'); + expect(model.context.id).toBeFalsy(); + expect(model.context.submit).toBeDefined(); + }); + }); + + describe('Post initialize model (create l7 rule)', function() { + + beforeEach(function() { + includeChildResources = false; + model.initialize('l7rule', false, '1234', '5678'); + scope.$apply(); + }); + + it('should initialize model properties', function() { + expect(model.initializing).toBe(false); + expect(model.initialized).toBe(true); + expect(model.subnets.length).toBe(0); + expect(model.members.length).toBe(0); + expect(model.certificates.length).toBe(0); + expect(model.listenerPorts.length).toBe(0); + expect(model.spec).toBeDefined(); + expect(model.spec.loadbalancer_id).toBe('1234'); + expect(model.spec.parentResourceId).toBe('5678'); + expect(model.spec.loadbalancer).toBeDefined(); + expect(model.spec.listener).toBeDefined(); + expect(model.spec.pool).toBeDefined(); + expect(model.spec.members.length).toBe(0); + expect(model.spec.certificates).toEqual([]); + expect(model.spec.monitor).toBeDefined(); + expect(model.certificatesError).toBe(false); + }); + + it('should initialize invert', function() { + expect(model.spec.l7rule.invert).toBe(false); + }); + + it('should initialize context properties', function() { + expect(model.context.resource).toBe('l7rule'); + expect(model.context.id).toBeFalsy(); + expect(model.context.submit).toBeDefined(); + }); + }); + describe('Post initialize model (create pool with listener)', function() { beforeEach(function() { @@ -794,6 +913,53 @@ }); }); + describe('Post initialize model (edit l7 policy)', function() { + + beforeEach(function() { + model.initialize('l7policy', '1234', 'loadbalancerId', 'listenerId'); + scope.$apply(); + }); + + it('should initialize model properties', function() { + expect(model.initializing).toBe(false); + expect(model.initialized).toBe(true); + expect(model.spec).toBeDefined(); + expect(model.spec.loadbalancer_id).toBeDefined(); + expect(model.spec.l7policy.id).toBe('1234'); + expect(model.spec.l7policy.name).toBe('L7 Policy 1'); + expect(model.spec.l7policy.description).toBe('l7 policy description'); + }); + + it('should initialize context', function() { + expect(model.context.resource).toBe('l7policy'); + expect(model.context.id).toBeDefined(); + expect(model.context.submit).toBeDefined(); + }); + }); + + describe('Post initialize model (edit l7 rule)', function() { + + beforeEach(function() { + model.initialize('l7rule', '1234', 'loadbalancerId', 'l7policyId'); + scope.$apply(); + }); + + it('should initialize model properties', function() { + expect(model.initializing).toBe(false); + expect(model.initialized).toBe(true); + expect(model.spec).toBeDefined(); + expect(model.spec.loadbalancer_id).toBeDefined(); + expect(model.spec.l7rule.id).toBe('1234'); + expect(model.spec.l7rule.type).toBe('HOST_NAME'); + }); + + it('should initialize context', function() { + expect(model.context.resource).toBe('l7rule'); + expect(model.context.id).toBeDefined(); + expect(model.context.submit).toBeDefined(); + }); + }); + describe('Post initialize model (edit pool)', function() { beforeEach(function() { @@ -1005,9 +1171,11 @@ // This is here to ensure that as people add/change spec properties, they don't forget // to implement tests for them. it('has the right number of properties', function() { - expect(Object.keys(model.spec).length).toBe(9); + expect(Object.keys(model.spec).length).toBe(11); expect(Object.keys(model.spec.loadbalancer).length).toBe(5); expect(Object.keys(model.spec.listener).length).toBe(8); + expect(Object.keys(model.spec.l7policy).length).toBe(8); + expect(Object.keys(model.spec.l7rule).length).toBe(7); expect(Object.keys(model.spec.pool).length).toBe(8); expect(Object.keys(model.spec.monitor).length).toBe(10); expect(model.spec.members).toEqual([]); @@ -1716,6 +1884,47 @@ }); }); + describe('Model submit function (create l7 policy)', function() { + + beforeEach(function() { + model.initialize('l7policy', null, '1234', 'listener1'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + model.spec.l7policy.action = 'REJECT'; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer_id).toBe('1234'); + expect(finalSpec.parentResourceId).toBe('listener1'); + expect(finalSpec.loadbalancer).toBeUndefined(); + expect(finalSpec.listener).toBeDefined(); + + expect(finalSpec.l7policy.action).toBe('REJECT'); + }); + }); + + describe('Model submit function (create l7 rule)', function() { + + beforeEach(function() { + model.initialize('l7rule', null, '1234', 'l7policy1'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + model.spec.l7rule.type = 'PATH'; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer_id).toBe('1234'); + expect(finalSpec.parentResourceId).toBe('l7policy1'); + expect(finalSpec.loadbalancer).toBeUndefined(); + + expect(finalSpec.l7rule.type).toBe('PATH'); + }); + }); + describe('Model submit function (create pool)', function() { beforeEach(function() { @@ -1977,6 +2186,45 @@ }); }); + describe('Model submit function (edit l7 policy)', function() { + + beforeEach(function() { + model.initialize('l7policy', 'l7policy1', '1234', '1234'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer_id).toBe('1234'); + expect(finalSpec.parentResourceId).toBe('1234'); + expect(finalSpec.loadbalancer).toBeUndefined(); + expect(finalSpec.listener).toBeDefined(); + + expect(finalSpec.l7policy.action).toBe('REDIRECT_TO_URL'); + }); + + }); + + describe('Model submit function (edit l7 rule)', function() { + + beforeEach(function() { + model.initialize('l7rule', '1234', '1234', '1234'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer_id).toBe('1234'); + expect(finalSpec.parentResourceId).toBe('1234'); + expect(finalSpec.loadbalancer).toBeUndefined(); + + expect(finalSpec.l7rule.type).toBe('HOST_NAME'); + }); + + }); + describe('Model submit function (edit pool)', function() { beforeEach(function() { diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.js index 10ae18e6..e04f312c 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.js @@ -42,6 +42,20 @@ helpUrl: basePath + 'workflow/listener/listener.help.html', formName: 'listenerDetailsForm' }, + { + id: 'l7policy', + title: gettext('L7 Policy Details'), + templateUrl: basePath + 'workflow/l7policy/l7policy.html', + helpUrl: basePath + 'workflow/l7policy/l7policy.help.html', + formName: 'l7policyDetailsForm' + }, + { + id: 'l7rule', + title: gettext('L7 Rule Details'), + templateUrl: basePath + 'workflow/l7rule/l7rule.html', + helpUrl: basePath + 'workflow/l7rule/l7rule.help.html', + formName: 'l7ruleDetailsForm' + }, { id: 'pool', title: gettext('Pool Details'), diff --git a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.spec.js b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.spec.js index 14ad72cc..22e73c02 100644 --- a/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.spec.js +++ b/octavia_dashboard/static/dashboard/project/lbaasv2/workflow/workflow.service.spec.js @@ -44,11 +44,13 @@ it('should have default steps defined', function () { var workflow = workflowService('My Workflow'); expect(workflow.steps).toBeDefined(); - expect(workflow.steps.length).toBe(6); + expect(workflow.steps.length).toBe(8); var forms = [ 'loadBalancerDetailsForm', 'listenerDetailsForm', + 'l7policyDetailsForm', + 'l7ruleDetailsForm', 'poolDetailsForm', 'memberDetailsForm', 'monitorDetailsForm', diff --git a/releasenotes/notes/add-l7-support-05a790bc2965c38f.yaml b/releasenotes/notes/add-l7-support-05a790bc2965c38f.yaml new file mode 100644 index 00000000..5f19cd27 --- /dev/null +++ b/releasenotes/notes/add-l7-support-05a790bc2965c38f.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds L7 policy support to the dashboard.