summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorank <ank.svu@gmail.com>2016-05-11 17:43:16 +0530
committerank <ank.svu@gmail.com>2016-05-12 10:09:07 +0530
commita924945dce3a2f646db41359f7f2b6187c2c9e7b (patch)
treec77f83d7bcd2e960be6b2f104585b54e302ef664
parentf0bedbde6b08a3a4fb87905f2e4ae5fe419ffe0c (diff)
Allow group member to create with fixed IP
Notes
Notes (review): Code-Review+2: Sumit Naiksatam <sumitnaiksatam@gmail.com> Code-Review+2: Hemanth Ravi <hemanth.ravi@oneconvergence.com> Workflow+1: Hemanth Ravi <hemanth.ravi@oneconvergence.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Thu, 12 May 2016 21:22:56 +0000 Reviewed-on: https://review.openstack.org/315002 Project: openstack/group-based-policy-ui Branch: refs/heads/master
-rw-r--r--gbpui/panels/policytargets/templates/policytargets/_update_groups.html73
-rw-r--r--gbpui/panels/policytargets/urls.py3
-rw-r--r--gbpui/panels/policytargets/views.py32
-rw-r--r--gbpui/panels/policytargets/workflows.py68
-rw-r--r--gbpui/static/dashboard/js/member.js218
5 files changed, 367 insertions, 27 deletions
diff --git a/gbpui/panels/policytargets/templates/policytargets/_update_groups.html b/gbpui/panels/policytargets/templates/policytargets/_update_groups.html
index cb21648..23a7ec6 100644
--- a/gbpui/panels/policytargets/templates/policytargets/_update_groups.html
+++ b/gbpui/panels/policytargets/templates/policytargets/_update_groups.html
@@ -1,31 +1,70 @@
1{% load i18n %} 1{% load i18n %}
2 2
3<noscript><h3>{{ step }}</h3></noscript> 3<noscript><h3>{{ step }}</h3></noscript>
4<table class="table-fixed" id="networkListSortContainer"> 4<style type="text/css">
5 #subnets_table {
6 width: 100%
7 }
8 #subnets_table td {
9 border: 1px solid #466E85;
10 text-align: center;
11 padding: 3px;
12 }
13</style>
14<table class="table-fixed" id="groupListSortContainer" style="width:100%">
5 <tbody> 15 <tbody>
6 <tr> 16 <tr>
7 <td class="actions"> 17 <td class="actions">
8 <label id="selected_network_label">{% trans "Selected groups" %}</label> 18 <div class="form-group">
19 <label id="selected_group_label">{% trans "Selected Groups" %}</label>
20 <span class="help-icon" data-toggle="tooltip" data-placement="top"
21 title=""
22 data-original-title="Select one or more Groups from the Available Groups. To select, click on Group or drag and drop to the Selected Groups area. You may also change the order of Groups by dragging and dropping within the Selected Groups (ordering determines the order of NICs). You may optionally assign an IP address for a Group by clicking on the Group in the Selected Groups.">
23 <span class="fa fa-question-circle"></span>
24 </span>
25 </div>
9 <ul id="selected_network" class="networklist"> 26 <ul id="selected_network" class="networklist">
10 </ul> 27 </ul>
11 <label>{% trans "Available groups" %}</label> 28 <label>{% trans "Available Groups" %}</label>
12 <ul id="available_network" class="networklist"> 29 <ul id="available_group" class="networklist">
13 </ul> 30 </ul>
14 </td> 31 </td>
15 <td class="help_text"> 32 <td class="help_text" style="padding-left:20px">
16 <p>{% blocktrans %}Choose group from Available groups to Selected 33 <div id="fixed_ip_div" style="display:none">
17 groups by push button or drag and drop, you may change group order 34 {% trans "Subnet(s) available for Group '" %}<label id="group"></label>{% trans "'" %}
18 by drag and drop as well. {% endblocktrans %}</p> 35 <table id='subnets_table'>
36 <thead>
37 <tr>
38 <td>CIDR</td>
39 <td>Pool Start</td>
40 <td>Pool End</td>
41 </tr>
42 </thead>
43 <tbody></tbody>
44 </table>
45 <div class="form-group">
46 <label class="control-label required" for="fixed_ip">Assign IP</label>
47 <span class="help-icon" data-toggle="tooltip" data-placement="top"
48 title=""
49 data-original-title="Choose IP address in Subnet’s CIDR within allocated pool start and end range (refer to table above).">
50 <span class="fa fa-question-circle"></span>
51 </span>
52 </div>
53 <input id="fixed_ip" type="text"/>
54 <input id="set_ip_button" type="button" value="Add"/>
55 <input id="remove" type="button" value="Remove"/>
56 <p id="errors" style="color:red" ></p>
57 </div>
19 </td> 58 </td>
20 </tr> 59 </tr>
21 </tbody> 60 </tbody>
22</table> 61</table>
23 62
24<table class="table-fixed" id="networkListIdContainer"> 63<table class="table-fixed" id="groupListIdContainer">
25 <tbody> 64 <tbody>
26 <tr> 65 <tr>
27 <td class="actions"> 66 <td class="actions">
28 <div id="networkListId"> 67 <div id="groupListId">
29 {% include "horizon/common/_form_fields.html" %} 68 {% include "horizon/common/_form_fields.html" %}
30 </div> 69 </div>
31 </td> 70 </td>
@@ -36,13 +75,7 @@
36 </tbody> 75 </tbody>
37</table> 76</table>
38 77
39 78 <script src='{{ STATIC_URL }}dashboard/js/member.js' type='text/javascript'></script>
40<script> 79 <script>
41 if (typeof $ !== 'undefined') { 80 member.groups_init();
42 horizon.instances.workflow_init($(".workflow")); 81 </script>
43 } else {
44 addHorizonLoadEvent(function() {
45 horizon.instances.workflow_init($(".workflow"));
46 });
47 }
48</script>
diff --git a/gbpui/panels/policytargets/urls.py b/gbpui/panels/policytargets/urls.py
index 21cf3ac..cf25422 100644
--- a/gbpui/panels/policytargets/urls.py
+++ b/gbpui/panels/policytargets/urls.py
@@ -73,4 +73,7 @@ urlpatterns = patterns('',
73 '(?P<ext_policy_target_id>[^/]+)/$', 73 '(?P<ext_policy_target_id>[^/]+)/$',
74 views.ExtRemoveConsumedPRSView.as_view(), 74 views.ExtRemoveConsumedPRSView.as_view(),
75 name='ext_remove_consumed_prs'), 75 name='ext_remove_consumed_prs'),
76 url(r'/check_ip_availability',
77 views.check_ip_availability,
78 name='check_ip_availability'),
76 ) 79 )
diff --git a/gbpui/panels/policytargets/views.py b/gbpui/panels/policytargets/views.py
index 8cd3093..92a241d 100644
--- a/gbpui/panels/policytargets/views.py
+++ b/gbpui/panels/policytargets/views.py
@@ -10,9 +10,11 @@
10# License for the specific language governing permissions and limitations 10# License for the specific language governing permissions and limitations
11# under the License. 11# under the License.
12 12
13import json
13import re 14import re
14 15
15from django.core.urlresolvers import reverse_lazy 16from django.core.urlresolvers import reverse_lazy
17from django.http import HttpResponse # noqa
16from django.utils.translation import ugettext_lazy as _ 18from django.utils.translation import ugettext_lazy as _
17 19
18from horizon import exceptions 20from horizon import exceptions
@@ -28,6 +30,11 @@ import forms as policy_target_forms
28import tabs as policy_target_tabs 30import tabs as policy_target_tabs
29import workflows as policy_target_workflows 31import workflows as policy_target_workflows
30 32
33from openstack_dashboard import api
34
35from netaddr import IPAddress
36from netaddr import IPNetwork
37
31PTGTabs = policy_target_tabs.PTGTabs 38PTGTabs = policy_target_tabs.PTGTabs
32PTGDetailsTabs = policy_target_tabs.PTGDetailsTabs 39PTGDetailsTabs = policy_target_tabs.PTGDetailsTabs
33 40
@@ -285,3 +292,28 @@ class RemoveConsumedPRSView(forms.ModalFormView):
285 292
286 def get_initial(self): 293 def get_initial(self):
287 return self.kwargs 294 return self.kwargs
295
296
297def check_ip_availability(request):
298 fixed_ip = request.GET.get('fixed_ip')
299 response = {'error': 'IP address is not within the allocated pool range'}
300 subnets = request.GET.get('subnets')
301 subnets = subnets.split(";")
302 for subnet in subnets:
303 subnet_details = subnet.split(",")
304 try:
305 if IPAddress(fixed_ip) in IPNetwork(subnet_details[0]):
306 if IPAddress(fixed_ip) >= IPAddress(subnet_details[1]) and \
307 IPAddress(fixed_ip) <= IPAddress(subnet_details[2]):
308 fixed_ips = "ip_address=" + fixed_ip
309 ports = api.neutron.port_list(request, fixed_ips=fixed_ips)
310 if ports:
311 response = {"inuse": False,
312 "error": "IP address already in use"}
313 else:
314 response = {"inuse": True}
315 break
316 except Exception:
317 response = {'error': 'Unable to check IP availability'}
318 json_string = json.dumps(response, ensure_ascii=False)
319 return HttpResponse(json_string, content_type='text/json')
diff --git a/gbpui/panels/policytargets/workflows.py b/gbpui/panels/policytargets/workflows.py
index 6a9e234..0a9f95c 100644
--- a/gbpui/panels/policytargets/workflows.py
+++ b/gbpui/panels/policytargets/workflows.py
@@ -34,6 +34,10 @@ from openstack_dashboard.dashboards.project.instances.workflows \
34from gbpui import client 34from gbpui import client
35from gbpui import fields 35from gbpui import fields
36 36
37from netaddr import IPAddress
38from netaddr import IPNetwork
39
40
37LOG = logging.getLogger(__name__) 41LOG = logging.getLogger(__name__)
38 42
39POLICY_RULE_SET_URL = "horizon:project:application_policy:addpolicy_rule_set" 43POLICY_RULE_SET_URL = "horizon:project:application_policy:addpolicy_rule_set"
@@ -389,7 +393,7 @@ class SetAccessControls(workflows.Step):
389class SetGroupAction(workflows.Action): 393class SetGroupAction(workflows.Action):
390 # To reuse horizon instance launch code related to Networking, 394 # To reuse horizon instance launch code related to Networking,
391 # form filed must be 'network' only 395 # form filed must be 'network' only
392 network = forms.MultipleChoiceField(label=_("Groups"), 396 network = fields.CustomMultiChoiceField(label=_("Groups"),
393 widget=forms.CheckboxSelectMultiple(), 397 widget=forms.CheckboxSelectMultiple(),
394 error_messages={ 398 error_messages={
395 'required': _( 399 'required': _(
@@ -403,7 +407,26 @@ class SetGroupAction(workflows.Action):
403 def __init__(self, request, *args, **kwargs): 407 def __init__(self, request, *args, **kwargs):
404 super(SetGroupAction, self).__init__(request, *args, **kwargs) 408 super(SetGroupAction, self).__init__(request, *args, **kwargs)
405 policy_targetid = self.request.path.split("/")[-2] 409 policy_targetid = self.request.path.split("/")[-2]
406 self.fields['network'].initial = [policy_targetid] 410 ptg = client.policy_target_get(request, policy_targetid)
411 subnet_dedails = None
412 for subnet_id in ptg.subnets:
413 try:
414 subnet = api.neutron.subnet_get(request, subnet_id)
415 if subnet_dedails is None:
416 subnet_dedails = subnet['cidr']
417 else:
418 subnet_dedails = ";" + subnet['cidr']
419 allocation_pools = subnet['allocation_pools']
420 if allocation_pools:
421 start = allocation_pools[0]['start']
422 end = allocation_pools[0]['end']
423 subnet_dedails = subnet_dedails + "," + start
424 subnet_dedails = subnet_dedails + "," + end
425 except Exception as e:
426 LOG.error(str(e))
427 pass
428 initial_value = policy_targetid + ":" + subnet_dedails
429 self.fields['network'].initial = [initial_value]
407 430
408 class Meta(object): 431 class Meta(object):
409 name = _("Groups") 432 name = _("Groups")
@@ -416,6 +439,24 @@ class SetGroupAction(workflows.Action):
416 tenant_id=request.user.tenant_id) 439 tenant_id=request.user.tenant_id)
417 for pt in pts: 440 for pt in pts:
418 pt.set_id_as_name_if_empty() 441 pt.set_id_as_name_if_empty()
442 subnet_dedails = None
443 for subnet_id in pt.subnets:
444 try:
445 subnet = api.neutron.subnet_get(request, subnet_id)
446 if subnet_dedails is None:
447 subnet_dedails = subnet['cidr']
448 else:
449 subnet_dedails = ";" + subnet['cidr']
450 allocation_pools = subnet['allocation_pools']
451 if allocation_pools:
452 start = allocation_pools[0]['start']
453 end = allocation_pools[0]['end']
454 subnet_dedails = subnet_dedails + "," + start
455 subnet_dedails = subnet_dedails + "," + end
456 except Exception as e:
457 LOG.error(str(e))
458 pass
459 pt.id = pt.id + ":" + subnet_dedails
419 pt_list.append((pt.id, pt.name)) 460 pt_list.append((pt.id, pt.name))
420 return sorted(pt_list, key=lambda obj: obj[1]) 461 return sorted(pt_list, key=lambda obj: obj[1])
421 except Exception: 462 except Exception:
@@ -534,9 +575,22 @@ class LaunchInstance(workflows.Workflow):
534 instance_name = context['name'] + str(count) 575 instance_name = context['name'] + str(count)
535 nics = [] 576 nics = []
536 for ptg_id in context['group_id']: 577 for ptg_id in context['group_id']:
537 ep = client.pt_create( 578 values = ptg_id.split(":")
538 request, policy_target_group_id=ptg_id, 579 ptg_id = values[0]
539 name=instance_name[:41] + "_gbpui") 580 args = {'policy_target_group_id': ptg_id,
581 'name': instance_name[:41] + "_gbpui"}
582 if len(values) == 3:
583 ptg = client.policy_target_get(request, ptg_id)
584 fixed_ip = values[2]
585 for subnet_id in ptg.subnets:
586 subnet = api.neutron.subnet_get(request, subnet_id)
587 if IPAddress(fixed_ip) in \
588 IPNetwork(subnet['cidr']):
589 args['fixed_ips'] = [
590 {'subnet_id': subnet['id'],
591 'ip_address': fixed_ip}]
592 break
593 ep = client.pt_create(request, **args)
540 nics.append({'port-id': ep.port_id}) 594 nics.append({'port-id': ep.port_id})
541 api.nova.server_create(request, 595 api.nova.server_create(request,
542 instance_name, 596 instance_name,
@@ -555,10 +609,10 @@ class LaunchInstance(workflows.Workflow):
555 config_drive=context.get('config_drive')) 609 config_drive=context.get('config_drive'))
556 count += 1 610 count += 1
557 return True 611 return True
558 except Exception: 612 except Exception as e:
559 error = _("Unable to launch member %(count)s with name %(name)s") 613 error = _("Unable to launch member %(count)s with name %(name)s")
560 msg = error % {'count': count, 'name': instance_name} 614 msg = error % {'count': count, 'name': instance_name}
561 LOG.error(msg) 615 LOG.error(str(e))
562 u = "horizon:project:policytargets:policy_targetdetails" 616 u = "horizon:project:policytargets:policy_targetdetails"
563 policy_target_id = self.request.path.split("/")[-2] 617 policy_target_id = self.request.path.split("/")[-2]
564 redirect = reverse(u, kwargs={'policy_target_id': 618 redirect = reverse(u, kwargs={'policy_target_id':
diff --git a/gbpui/static/dashboard/js/member.js b/gbpui/static/dashboard/js/member.js
new file mode 100644
index 0000000..69cf942
--- /dev/null
+++ b/gbpui/static/dashboard/js/member.js
@@ -0,0 +1,218 @@
1member = {
2 user_decided_length: false,
3 user_volume_size: false,
4 groups_selected: [],
5 groups_available: [],
6
7 /*
8 * Gets the html select element associated with a given
9 * group id for group_id.
10 **/
11 get_group_element: function(group_id) {
12 return $('li > label[for^="id_network_' + group_id + '"]');
13 },
14
15 /*
16 * Initializes an associative array of lists of the current
17 * groups.
18 **/
19 init_group_list: function () {
20 member.groups_selected = [];
21 member.groups_available = [];
22 $(this.get_group_element("")).each(function () {
23 var $this = $(this);
24 var $input = $this.children("input");
25 var name = horizon.string.escapeHtml($this.text().replace(/^\s+/, ""));
26 var group_property = {
27 "name": name,
28 "id": $input.attr("id"),
29 "value": $input.attr("value")
30 };
31 if ($input.is(":checked")) {
32 member.groups_selected.push(group_property);
33 } else {
34 member.groups_available.push(group_property);
35 }
36 });
37 },
38
39 /*
40 * Generates the HTML structure for a group that will be displayed
41 * as a list item in the group list.
42 **/
43 generate_group_element: function(name, id, value) {
44 var $li = $('<li>');
45 $li.attr('name', value).html(name + '<strong></strong><a href="#" class="btn btn-primary"></a>');
46 return $li;
47 },
48
49 /*
50 * Generates the HTML structure for the group List.
51 **/
52 generate_grouplist_html: function() {
53 var self = this;
54 var available_group = $("#available_group");
55 var selected_group = $("#selected_network");
56 var reset_unselected_group_fixed_ip = function(){
57 ptg = $("#fixed_ip").attr("data-ptg")
58 if($("ul#available_group li[name^='"+ ptg +"']").length > 0){
59 $("ul#available_group li[name^='"+ ptg +"']").css("background-color", "");
60 $("#fixed_ip_div").hide()
61 }
62 };
63 var updateForm = function() {
64 var groupListId = $("#groupListId");
65 var lists = groupListId.find("li").attr('data-index',100);
66 var active_groups = $("#selected_network > li").map(function(){
67 return $(this).attr("name");
68 });
69 groupListId.find("input:checkbox").removeAttr('checked');
70 active_groups.each(function(index, value){
71 groupListId.find("input:checkbox[value^='" + value + "']")
72 .prop('checked', true)
73 .parents("li").attr('data-index',index);
74 });
75 groupListId.find("ul").html(
76 lists.sort(function(a,b){
77 if( $(a).data("index") < $(b).data("index")) { return -1; }
78 if( $(a).data("index") > $(b).data("index")) { return 1; }
79 return 0;
80 })
81 );
82 reset_unselected_group_fixed_ip()
83 };
84
85 $("#groupListSortContainer").show();
86 $("#groupListIdContainer").hide();
87 self.init_group_list();
88 // Make sure we don't duplicate the groups in the list
89 available_group.empty();
90 $.each(self.groups_available, function(index, value){
91 available_group.append(self.generate_group_element(value.name, value.id, value.value));
92 });
93 // Make sure we don't duplicate the groups in the list
94 selected_group.empty();
95 $.each(self.groups_selected, function(index, value){
96 selected_group.append(self.generate_group_element(value.name, value.id, value.value));
97 });
98
99 $(".networklist > li > a.btn").click(function(e){
100 var $this = $(this);
101 e.preventDefault();
102 e.stopPropagation();
103 if($this.parents("ul#available_group").length > 0) {
104 $this.parent().appendTo(selected_group);
105 } else if ($this.parents("ul#selected_network").length > 0) {
106 $this.parent().appendTo(available_group);
107 }
108 updateForm();
109 });
110 if ($("#groupListId > div.form-group.error").length > 0) {
111 var errortext = $("#groupListId > div.form-group.error span.help-block").text();
112 $("#selected_group_label").before($('<div class="dynamic-error">').html(errortext));
113 }
114 $(".networklist").sortable({
115 connectWith: "ul.networklist",
116 placeholder: "ui-state-highlight",
117 distance: 5,
118 start:function(e,info){
119 selected_group.addClass("dragging");
120 },
121 stop:function(e,info){
122 selected_group.removeClass("dragging");
123 updateForm();
124 }
125 }).disableSelection();
126 },
127
128 allow_fixed_ip: function(selected_group){
129 fixed_ip = ""
130 $("#fixed_ip").val("")
131 $("#errors").text("")
132 $("#fixed_ip_div").show()
133 ptg = $(selected_group).attr('name')
134 $(selected_group).siblings().css( "background-color", "");
135 $(selected_group).css('background-color', '#e6e6e6');
136 selected_element = $(".multiple-checkbox #id_network li input[value^='"+ ptg +"']");
137 value = selected_element.val();
138 values = value.split(':');
139 group = $(selected_group).text()
140 group = group.split("(")
141 $("#group").text(group[0])
142 subnets = values[1].split(";")
143 $('#subnets_table tbody').empty();
144 for (index=0; index < subnets.length; index++){
145 subnet_details = subnets[index].split(",")
146 row = '<tr><td>'+subnet_details[0]+'</td><td>'+
147 subnet_details[1] +'</td><td>' + subnet_details[2]+'</td>';
148 $('#subnets_table tbody').append(row);
149 }
150 $("#fixed_ip").attr("data-ptg", values[0]);
151 $("#fixed_ip").attr("data-subnet", values[1])
152 if (values.length == 3)
153 $("#fixed_ip").val(values[2]);
154 },
155 associate_fixed_ip: function(){
156 ptg = $("#fixed_ip").attr("data-ptg")
157 subnet = $("#fixed_ip").attr("data-subnet")
158 fixed_ip = $("#fixed_ip").val()
159 if (!fixed_ip || 0 === fixed_ip.length ){
160 $("#errors").text("Enter valid IP address")
161 return
162 }
163 $.ajax({
164 url: 'check_ip_availability',
165 data: "fixed_ip="+fixed_ip+"&subnets="+subnet,
166 method: 'get',
167 success: function(response) {
168 if(response.inuse){
169 horizon.alert('success', "IP address '" + fixed_ip +"' is available.")
170 value = ptg + ":" + subnet + ":" + fixed_ip
171 selected_element = $(".multiple-checkbox #id_network li input[value^='"+ ptg +"']");
172 selected_element.val(value)
173 if(fixed_ip){
174 $("#selected_network li[name^='"+ ptg +"'] strong").text(" ( "+fixed_ip + " )")
175 }
176 else{
177 $("#selected_network li[name^='"+ ptg +"'] strong").text("")
178 }
179 $("ul#selected_network li[name^='"+ ptg +"']").css("background-color", "");
180 $("#fixed_ip_div").hide()
181 }
182 else{
183 $("#errors").text(response.error)
184 }
185 },
186 error: function(response) {
187 $("#errors").text(response)
188 }
189 });
190 },
191 disassociate_fixed_ip: function(){
192 ptg = $("#fixed_ip").attr("data-ptg")
193 subnet = $("#fixed_ip").attr("data-subnet")
194 value = ptg + ":" + subnet
195 selected_element = $(".multiple-checkbox #id_network li input[value^='"+ ptg +"']");
196 selected_element.val(value)
197 $("#selected_network li[name^='"+ ptg +"'] strong").text("")
198 $("#fixed_ip_div").hide()
199 $("ul#selected_network li").css("background-color", "");
200 },
201 groups_init: function() {
202 // Initialise the drag and drop group list
203 member.generate_grouplist_html();
204
205 // allocate fixed ip
206 $(document).on('click', "ul#selected_network li", function(){
207 member.allow_fixed_ip(this);
208 });
209
210 $("#set_ip_button").click(function(){
211 member.associate_fixed_ip();
212 });
213
214 $("#remove").click(function(){
215 member.disassociate_fixed_ip()
216 });
217 }
218};