Browse Source

Allow group member to create with fixed IP

Change-Id: I0f00bc39a07eeb524c036c99b79b99bade480dcd
Implements: blueprint fixed-ips-for-pt
tags/5.0.0
ank 3 years ago
parent
commit
a924945dce

+ 53
- 20
gbpui/panels/policytargets/templates/policytargets/_update_groups.html View File

@@ -1,31 +1,70 @@
1 1
 {% load i18n %}
2 2
 
3 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 15
   <tbody>
6 16
     <tr>
7 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 26
         <ul id="selected_network" class="networklist">
10 27
         </ul>
11
-        <label>{% trans "Available groups" %}</label>
12
-        <ul id="available_network" class="networklist">
28
+        <label>{% trans "Available Groups" %}</label>
29
+        <ul id="available_group" class="networklist">
13 30
         </ul>
14 31
       </td>
15
-      <td class="help_text">
16
-          <p>{% blocktrans %}Choose group from Available groups to Selected 
17
-              groups by push button or drag and drop, you may change group order 
18
-              by drag and drop as well. {% endblocktrans %}</p>
32
+      <td class="help_text" style="padding-left:20px">
33
+            <div id="fixed_ip_div" style="display:none">
34
+              {% trans "Subnet(s) available for Group '" %}<label id="group"></label>{% trans "'" %}
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 58
       </td>
20 59
     </tr>
21 60
   </tbody>
22 61
 </table>
23 62
 
24
-<table class="table-fixed" id="networkListIdContainer">
63
+<table class="table-fixed" id="groupListIdContainer">
25 64
   <tbody>
26 65
     <tr>
27 66
       <td class="actions">
28
-          <div id="networkListId">
67
+          <div id="groupListId">
29 68
             {% include "horizon/common/_form_fields.html" %}
30 69
           </div>
31 70
       </td>
@@ -36,13 +75,7 @@
36 75
   </tbody>
37 76
 </table>
38 77
 
39
-
40
-<script>
41
-  if (typeof $ !== 'undefined') {
42
-    horizon.instances.workflow_init($(".workflow"));
43
-  } else {
44
-    addHorizonLoadEvent(function() {
45
-      horizon.instances.workflow_init($(".workflow"));
46
-    });
47
-  }
48
-</script>
78
+  <script src='{{ STATIC_URL }}dashboard/js/member.js' type='text/javascript'></script>
79
+  <script>
80
+  member.groups_init();
81
+  </script>

+ 3
- 0
gbpui/panels/policytargets/urls.py View File

@@ -73,4 +73,7 @@ urlpatterns = patterns('',
73 73
                            '(?P<ext_policy_target_id>[^/]+)/$',
74 74
                            views.ExtRemoveConsumedPRSView.as_view(),
75 75
                            name='ext_remove_consumed_prs'),
76
+                       url(r'/check_ip_availability',
77
+                           views.check_ip_availability,
78
+                           name='check_ip_availability'),
76 79
                        )

+ 32
- 0
gbpui/panels/policytargets/views.py View File

@@ -10,9 +10,11 @@
10 10
 #    License for the specific language governing permissions and limitations
11 11
 #    under the License.
12 12
 
13
+import json
13 14
 import re
14 15
 
15 16
 from django.core.urlresolvers import reverse_lazy
17
+from django.http import HttpResponse  # noqa
16 18
 from django.utils.translation import ugettext_lazy as _
17 19
 
18 20
 from horizon import exceptions
@@ -28,6 +30,11 @@ import forms as policy_target_forms
28 30
 import tabs as policy_target_tabs
29 31
 import workflows as policy_target_workflows
30 32
 
33
+from openstack_dashboard import api
34
+
35
+from netaddr import IPAddress
36
+from netaddr import IPNetwork
37
+
31 38
 PTGTabs = policy_target_tabs.PTGTabs
32 39
 PTGDetailsTabs = policy_target_tabs.PTGDetailsTabs
33 40
 
@@ -285,3 +292,28 @@ class RemoveConsumedPRSView(forms.ModalFormView):
285 292
 
286 293
     def get_initial(self):
287 294
         return self.kwargs
295
+
296
+
297
+def 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')

+ 61
- 7
gbpui/panels/policytargets/workflows.py View File

@@ -34,6 +34,10 @@ from openstack_dashboard.dashboards.project.instances.workflows \
34 34
 from gbpui import client
35 35
 from gbpui import fields
36 36
 
37
+from netaddr import IPAddress
38
+from netaddr import IPNetwork
39
+
40
+
37 41
 LOG = logging.getLogger(__name__)
38 42
 
39 43
 POLICY_RULE_SET_URL = "horizon:project:application_policy:addpolicy_rule_set"
@@ -389,7 +393,7 @@ class SetAccessControls(workflows.Step):
389 393
 class SetGroupAction(workflows.Action):
390 394
     # To reuse horizon instance launch code related to Networking,
391 395
     # form filed must be 'network' only
392
-    network = forms.MultipleChoiceField(label=_("Groups"),
396
+    network = fields.CustomMultiChoiceField(label=_("Groups"),
393 397
                                         widget=forms.CheckboxSelectMultiple(),
394 398
                                         error_messages={
395 399
                                             'required': _(
@@ -403,7 +407,26 @@ class SetGroupAction(workflows.Action):
403 407
     def __init__(self, request, *args, **kwargs):
404 408
         super(SetGroupAction, self).__init__(request, *args, **kwargs)
405 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 431
     class Meta(object):
409 432
         name = _("Groups")
@@ -416,6 +439,24 @@ class SetGroupAction(workflows.Action):
416 439
                 tenant_id=request.user.tenant_id)
417 440
             for pt in pts:
418 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 460
                 pt_list.append((pt.id, pt.name))
420 461
             return sorted(pt_list, key=lambda obj: obj[1])
421 462
         except Exception:
@@ -534,9 +575,22 @@ class LaunchInstance(workflows.Workflow):
534 575
                     instance_name = context['name'] + str(count)
535 576
                 nics = []
536 577
                 for ptg_id in context['group_id']:
537
-                    ep = client.pt_create(
538
-                        request, policy_target_group_id=ptg_id,
539
-                        name=instance_name[:41] + "_gbpui")
578
+                    values = ptg_id.split(":")
579
+                    ptg_id = values[0]
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 594
                     nics.append({'port-id': ep.port_id})
541 595
                 api.nova.server_create(request,
542 596
                                    instance_name,
@@ -555,10 +609,10 @@ class LaunchInstance(workflows.Workflow):
555 609
                                    config_drive=context.get('config_drive'))
556 610
                 count += 1
557 611
             return True
558
-        except Exception:
612
+        except Exception as e:
559 613
             error = _("Unable to launch member %(count)s with name %(name)s")
560 614
             msg = error % {'count': count, 'name': instance_name}
561
-            LOG.error(msg)
615
+            LOG.error(str(e))
562 616
             u = "horizon:project:policytargets:policy_targetdetails"
563 617
             policy_target_id = self.request.path.split("/")[-2]
564 618
             redirect = reverse(u, kwargs={'policy_target_id':

+ 218
- 0
gbpui/static/dashboard/js/member.js View File

@@ -0,0 +1,218 @@
1
+member = {
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
+};

Loading…
Cancel
Save