staypuft 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +13 -5
  2. data/app/assets/javascripts/staypuft/new_subnet.js +9 -0
  3. data/app/assets/javascripts/staypuft/staypuft.js +39 -0
  4. data/app/assets/javascripts/staypuft/subnets_assignment.js +5 -1
  5. data/app/controllers/staypuft/concerns/hosts_api_extensions.rb +18 -0
  6. data/app/controllers/staypuft/concerns/hosts_controller_extensions.rb +38 -0
  7. data/app/controllers/staypuft/subnets_controller.rb +25 -0
  8. data/app/lib/staypuft/seeder.rb +71 -19
  9. data/app/models/staypuft/deployment.rb +11 -2
  10. data/app/models/staypuft/deployment/cinder_service.rb +1 -1
  11. data/app/models/staypuft/deployment/cinder_service/equallogic.rb +1 -23
  12. data/app/models/staypuft/deployment/ip_address_validator.rb +30 -0
  13. data/app/models/staypuft/deployment/neutron_service.rb +55 -9
  14. data/app/models/staypuft/deployment/neutron_service/cisconexus.rb +1 -12
  15. data/app/models/staypuft/simple_subnet.rb +115 -0
  16. data/app/overrides/foreman_hosts_edit.rb +13 -0
  17. data/app/overrides/select_multiple_systems_hostgroup.rb +15 -0
  18. data/app/views/staypuft/steps/_neutron.html.erb +35 -22
  19. data/app/views/staypuft/steps/deployment_settings.html.erb +2 -0
  20. data/app/views/staypuft/steps/network_configuration.html.erb +28 -1
  21. data/app/views/staypuft/subnets/_form.html.erb +24 -0
  22. data/app/views/staypuft/subnets/create.js.erb +14 -0
  23. data/app/views/staypuft/subnets/new.html.erb +3 -0
  24. data/config/routes.rb +4 -0
  25. data/db/migrate/20141009064907_add_custom_repos_to_deployment.rb +5 -0
  26. data/lib/staypuft/engine.rb +5 -2
  27. data/lib/staypuft/version.rb +1 -1
  28. metadata +151 -140
@@ -4,11 +4,13 @@ module Staypuft
4
4
  'neutron'
5
5
  end
6
6
 
7
- SEGMENTATION_LIST = ['vxlan', 'vlan', 'gre', 'flat']
8
- VLAN_HELP = N_('[1-4094] (e.g. 10:15)')
9
- ML2MECHANISM_TYPES = :ml2_openvswitch, :ml2_l2population, :ml2_cisco_nexus
7
+ SEGMENTATION_LIST = ['vxlan', 'vlan', 'gre', 'flat']
8
+ VLAN_HELP = N_('[1-4094] (e.g. 10:15)')
9
+ ML2MECHANISM_TYPES = :ml2_openvswitch, :ml2_l2population, :ml2_cisco_nexus
10
+ N1KV_PARAMS = :n1kv_vsm_ip, :n1kv_vsm_password
10
11
 
11
- param_attr :network_segmentation, :tenant_vlan_ranges, *ML2MECHANISM_TYPES
12
+ param_attr :network_segmentation, :tenant_vlan_ranges, :core_plugin,
13
+ *ML2MECHANISM_TYPES, *N1KV_PARAMS
12
14
  param_attr_array :nexuses => Cisconexus
13
15
 
14
16
  module NetworkSegmentation
@@ -26,6 +28,19 @@ module Staypuft
26
28
 
27
29
  validates :network_segmentation, presence: true, inclusion: { in: NetworkSegmentation::TYPES }
28
30
 
31
+ module CorePlugin
32
+ ML2 = 'ml2'
33
+ N1KV = 'n1kv'
34
+ LABELS = { ML2 => N_("ML2 Core Plugin"),
35
+ N1KV => N_("N1KV Core Plugin") }
36
+ MODULES = { ML2 => 'neutron.plugins.ml2.plugin.Ml2Plugin',
37
+ N1KV => 'neutron.plugins.cisco.network_plugin.PluginV2'}
38
+ TYPES = LABELS.keys
39
+ HUMAN = N_('Core Plugin Type')
40
+ end
41
+
42
+ validates :core_plugin, presence: true, inclusion: { in: CorePlugin::TYPES }
43
+
29
44
  module TenantVlanRanges
30
45
  HUMAN = N_('Tenant (VM Data) VLAN Ranges')
31
46
  HUMAN_AFTER = VLAN_HELP
@@ -40,6 +55,18 @@ module Staypuft
40
55
  :if => :vlan_segmentation?,
41
56
  :neutron_vlan_ranges => true
42
57
 
58
+ class N1kvIpAddressValidator < ActiveModel::EachValidator
59
+ include Staypuft::Deployment::IpAddressValidator
60
+ end
61
+
62
+ validates :n1kv_vsm_ip,
63
+ :presence => true,
64
+ :if => :n1kv_plugin?,
65
+ :n1kv_ip_address => true
66
+ validates :n1kv_vsm_password,
67
+ :presence => true,
68
+ :if => :n1kv_plugin?
69
+
43
70
  module Ml2Mechanisms
44
71
  OPENVSWITCH = 'openvswitch'
45
72
  L2POPULATION = 'l2population'
@@ -50,23 +77,27 @@ module Staypuft
50
77
  TYPES = LABELS.keys
51
78
  HUMAN = N_('ML2 Mechanism Drivers')
52
79
  end
53
- validate :at_least_one_mechanism_selected
80
+ validate :at_least_one_mechanism_selected,
81
+ :if => :ml2_plugin?
54
82
  validate :cisco_nexuses,
55
- :if => :cisco_nexus_mechanism?
83
+ :if => [:ml2_plugin?, :cisco_nexus_mechanism?]
56
84
  validates :nexuses,
57
85
  :presence => true,
58
- :if => :cisco_nexus_mechanism?
86
+ :if => [:ml2_plugin?, :cisco_nexus_mechanism?]
59
87
 
60
88
  class Jail < Safemode::Jail
61
89
  allow :networker_vlan_ranges, :compute_vlan_ranges, :network_segmentation, :enable_tunneling?,
62
90
  :tenant_iface, :networker_ovs_bridge_mappings, :networker_ovs_bridge_uplinks,
63
91
  :compute_ovs_bridge_mappings, :compute_ovs_bridge_uplinks, :ovs_tunnel_types,
64
92
  :openvswitch_mechanism?, :l2population_mechanism?, :cisco_nexus_mechanism?,
65
- :ml2_mechanisms, :nexuses, :active?, :compute_cisco_nexus_config
93
+ :ml2_mechanisms, :nexuses, :active?, :compute_cisco_nexus_config, :core_plugin,
94
+ :ml2_plugin?, :n1kv_plugin?, :n1kv_vsm_ip, :n1kv_vsm_password,
95
+ :core_plugin_module
66
96
  end
67
97
 
68
98
  def set_defaults
69
99
  self.network_segmentation = NetworkSegmentation::VXLAN
100
+ self.core_plugin = CorePlugin::ML2
70
101
  self.ml2_openvswitch = "true"
71
102
  self.ml2_l2population = "true"
72
103
  self.ml2_cisco_nexus = "false"
@@ -145,6 +176,18 @@ module Staypuft
145
176
  self.ml2_cisco_nexus == "true"
146
177
  end
147
178
 
179
+ def ml2_plugin?
180
+ self.core_plugin == CorePlugin::ML2
181
+ end
182
+
183
+ def n1kv_plugin?
184
+ self.core_plugin == CorePlugin::N1KV
185
+ end
186
+
187
+ def core_plugin_module
188
+ CorePlugin::MODULES[self.core_plugin]
189
+ end
190
+
148
191
  def compute_cisco_nexus_config
149
192
  Hash[nexuses.map { |nexus| [nexus.hostname, nexus.config_hash] }]
150
193
  end
@@ -155,11 +198,14 @@ module Staypuft
155
198
 
156
199
  def param_hash
157
200
  { 'network_segmentation' => network_segmentation,
201
+ 'core_plugin' => core_plugin,
158
202
  'tenant_vlan_ranges' => tenant_vlan_ranges,
159
203
  'ml2_openvswitch' => ml2_openvswitch,
160
204
  'ml2_l2population' => ml2_l2population,
161
205
  'ml2_cisco_nexus' => ml2_cisco_nexus,
162
- 'nexuses' => nexuses }
206
+ 'nexuses' => nexuses,
207
+ 'n1kv_vsm_ip' => n1kv_vsm_ip,
208
+ 'n1kv_vsm_password' => n1kv_vsm_password }
163
209
  end
164
210
 
165
211
  private
@@ -29,18 +29,7 @@ module Staypuft
29
29
  end
30
30
 
31
31
  class IpValueValidator < ActiveModel::EachValidator
32
- def validate_each(record, attribute, value)
33
- return if value.empty?
34
-
35
- ip_addr = IPAddr.new(value)
36
- ip_range = ip_addr.to_range
37
- if ip_range.begin == ip_range.end
38
- true
39
- else
40
- record.errors.add attribute, "Specify single IP address, not range"
41
- false
42
- end
43
- end
32
+ include Staypuft::Deployment::IpAddressValidator
44
33
  end
45
34
 
46
35
  class PortMapValueValidator < ActiveModel::EachValidator
@@ -0,0 +1,115 @@
1
+ require 'ipaddress'
2
+
3
+ module Staypuft
4
+ class SimpleSubnet
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::AttributeMethods
7
+ include ActiveModel::Validations
8
+
9
+ DHCP_SERVER_TYPE = {
10
+ 'external' => 'External DHCP',
11
+ 'none' => 'No existing DHCP'
12
+ }
13
+ ATTRS = [:name, :dhcp_server, :network_address, :vlan, :gateway,
14
+ :ip_range_from, :ip_range_to]
15
+ attr_accessor *ATTRS
16
+ attr_accessor :subnet, :deployment
17
+ delegate :to_key, :to => :subnet, :allow_nil => true
18
+
19
+ validates :name, :dhcp_server, :network_address, presence: true
20
+ validates :gateway, :presence => true,
21
+ :if => Proc.new { |subnet| subnet.dhcp_server == 'none' }
22
+ validates_format_of :gateway, :with => Net::Validations::IP_REGEXP,
23
+ :if => Proc.new { |subnet| subnet.dhcp_server == 'none' }
24
+ validate :validate_network_address
25
+ validate :validate_ranges, :if => Proc.new { |subnet| subnet.dhcp_server == 'none' }
26
+
27
+ def initialize(attrs={})
28
+ self.dhcp_server = 'external'
29
+ if attrs.is_a?(::Subnet)
30
+ @subnet = Subnet.find_by_name(attrs.name)
31
+ convert_attributes_from
32
+ else
33
+ @subnet = Subnet.new
34
+ ATTRS.each do |attr|
35
+ self.send("#{attr}=", attrs.has_key?(attr) ? attrs[attr] : nil)
36
+ end
37
+ end
38
+ end
39
+
40
+ def save
41
+ if self.valid?
42
+ @subnet = Subnet.find_or_initialize_by_name(name)
43
+ convert_attributes_to
44
+ @subnet.save
45
+ else
46
+ false
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def convert_attributes_to
53
+ @subnet.boot_mode = self.dhcp_server == 'external' ? ::Subnet::BOOT_MODES[:dhcp] : ::Subnet::BOOT_MODES[:static]
54
+ @subnet.ipam = self.dhcp_server == 'external' ? ::Subnet::IPAM_MODES[:none] : ::Subnet::IPAM_MODES[:db]
55
+ @subnet.network = get_network
56
+ @subnet.mask = get_mask
57
+ @subnet.vlanid = self.vlan
58
+ if self.dhcp_server == 'none'
59
+ @subnet.gateway = self.gateway
60
+ @subnet.from = self.ip_range_from
61
+ @subnet.to = self.ip_range_to
62
+ end
63
+ end
64
+
65
+ def convert_attributes_from
66
+ self.name = @subnet.name
67
+ if @subnet.boot_mode == ::Subnet::BOOT_MODES[:dhcp] && @subnet.ipam == ::Subnet::IPAM_MODES[:none]
68
+ self.dhcp_server = 'external'
69
+ elsif @subnet.boot_mode == ::Subnet::BOOT_MODES[:static] && @subnet.ipam == ::Subnet::IPAM_MODES[:db]
70
+ self.dhcp_server = 'none'
71
+ else
72
+ raise 'unknown simple subnet combination'
73
+ end
74
+
75
+ self.network_address = @subnet.network_address
76
+ self.gateway = @subnet.gateway
77
+ self.vlan = @subnet.vlanid
78
+ self.ip_range_from = @subnet.from
79
+ self.ip_range_to = @subnet.to
80
+ end
81
+
82
+ def get_network
83
+ IPAddress::IPv4.new(self.network_address).network.address
84
+ rescue Exception => ex
85
+ Rails.logger.warn("failed to parse network address: #{ex.message}")
86
+ nil
87
+ end
88
+
89
+ def get_mask
90
+ IPAddress::IPv4.new(self.network_address).netmask
91
+ rescue Exception => ex
92
+ Rails.logger.warn("failed to parse network mask: #{ex.message}")
93
+ nil
94
+ end
95
+
96
+ def validate_network_address
97
+ network = get_network
98
+ mask = get_mask
99
+ if !network || !mask || !(network =~ Net::Validations::IP_REGEXP) ||
100
+ !(mask =~ Net::Validations::MASK_REGEXP)
101
+ errors.add(:network_address, _("invalid Network Address"))
102
+ end
103
+ end
104
+
105
+ def validate_ranges
106
+ errors.add(:ip_range_from, _("invalid IP address")) if ip_range_from.present? && !(ip_range_from =~ Net::Validations::IP_REGEXP)
107
+ errors.add(:ip_range_to, _("invalid IP address")) if ip_range_to.present? && !(ip_range_to =~ Net::Validations::IP_REGEXP)
108
+ if ip_range_from.present? or ip_range_to.present?
109
+ errors.add(:ip_range_from, _("must be specified if 'IP Range End' is defined")) if ip_range_from.blank?
110
+ errors.add(:ip_range_to, _("must be specified if 'IP Range Start' is defined")) if ip_range_to.blank?
111
+ end
112
+ end
113
+
114
+ end
115
+ end
@@ -22,3 +22,16 @@ Deface::Override.new(:virtual_path => "hosts/_unattended",
22
22
  :insert_after => "#network",
23
23
  :text => "<div class='tab-pane' id='fencing'><%= render 'hosts/fencing', :host => @host, :f => f %></div>"
24
24
  )
25
+
26
+ Deface::Override.new(:virtual_path => "hosts/_form",
27
+ :name => "remove_openstack_hostgroups",
28
+ :replace => "#{erb_tag}:contains(':hostgroup_id')",
29
+ :original => '04903c858c6f96ed5c015cac5960e02708d8fea8'
30
+ ) do
31
+ " <%= select_f f, :hostgroup_id, accessible_hostgroups.reject { |hg| hg.to_label =~ /#{Setting[:base_hostgroup]}\\/.*/ }, :id, :to_label,
32
+ { :include_blank => true},
33
+ { :onchange => 'hostgroup_changed(this);', :'data-host-id' => @host.id,
34
+ :'data-url' => (@host.new_record? || @host.type_changed?) ? process_hostgroup_hosts_path : hostgroup_or_environment_selected_hosts_path,
35
+ :help_inline => :indicator } %>
36
+ "
37
+ end
@@ -0,0 +1,15 @@
1
+ # older deface requires code prefix for erb tags
2
+ if Gem.loaded_specs['deface'].version >= Gem::Version.new('1.0.0')
3
+ erb_tag = 'erb[loud]'
4
+ else
5
+ erb_tag = 'code[erb-loud]'
6
+ end
7
+
8
+ Deface::Override.new(:virtual_path => "hosts/select_multiple_hostgroup",
9
+ :name => "remove_openstack_hostgroups_multiple",
10
+ :replace => "#{erb_tag}:contains(':id')",
11
+ :original => '<%= selectable_f f, :id, [[_("Select host group"),"disabled"],[_("*Clear host group*"), "" ]] + Hostgroup.all.map{|h| [h.to_label, h.id]}.sort,{}, :onchange => "toggle_multiple_ok_button(this)" %>'
12
+ ) do
13
+ " <%= selectable_f f, :id, [[_(\"Select host group\"),\"disabled\"],[_(\"*Clear host group*\"), \"\" ]] + Hostgroup.all.reject{ |hg| hg.to_label =~ /#{Setting[:base_hostgroup]}\\/.*/ }.map{|h| [h.to_label, h.id]}.sort,{},
14
+ :onchange => \"toggle_multiple_ok_button(this)\" %>"
15
+ end
@@ -16,29 +16,42 @@
16
16
  help_inline: _(Staypuft::Deployment::NeutronService::TenantVlanRanges::HUMAN_AFTER)) %>
17
17
  </div>
18
18
 
19
- <div class="form-group">
20
- <label class="col-md-4 control-label"><%= _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::HUMAN) %></label>
21
- <div class="col-md-4">
22
- <%= check_box_f_non_inline(p, :ml2_openvswitch,
23
- :checked_value => 'true',
24
- :unchecked_value => 'false',
25
- :checked => @deployment.neutron.openvswitch_mechanism?,
26
- :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['openvswitch']))
27
- %>
28
- <%= check_box_f_non_inline(p, :ml2_l2population,
29
- :checked_value => 'true',
30
- :unchecked_value => 'false',
31
- :checked => @deployment.neutron.l2population_mechanism?,
32
- :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['l2population']))
33
- %>
34
- <%= check_box_f_non_inline(p, :ml2_cisco_nexus,
35
- :checked_value => 'true',
36
- :unchecked_value => 'false',
37
- :checked => @deployment.neutron.cisco_nexus_mechanism?,
38
- :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['cisco_nexus']))
39
- %>
40
- </div>
19
+ <%= change_label_width(4, field(p, :core_plugin, :label => _(Staypuft::Deployment::NeutronService::CorePlugin::HUMAN)) do
20
+ Staypuft::Deployment::NeutronService::CorePlugin::LABELS.map do |value, label|
21
+ radio_button_f_non_inline(p, :core_plugin,
22
+ :checked => @deployment.neutron.core_plugin == value,
23
+ :value => value,
24
+ :text => label)
25
+ end.join
26
+ end) %>
27
+
28
+ <div class="neutron_ml2_mechanisms inset_form hide">
29
+ <%= _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::HUMAN) %>
30
+ <%= check_box_f_non_inline(p, :ml2_openvswitch,
31
+ :checked_value => 'true',
32
+ :unchecked_value => 'false',
33
+ :checked => @deployment.neutron.openvswitch_mechanism?,
34
+ :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['openvswitch']))
35
+ %>
36
+ <%= check_box_f_non_inline(p, :ml2_l2population,
37
+ :checked_value => 'true',
38
+ :unchecked_value => 'false',
39
+ :checked => @deployment.neutron.l2population_mechanism?,
40
+ :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['l2population']))
41
+ %>
42
+ <%= check_box_f_non_inline(p, :ml2_cisco_nexus,
43
+ :checked_value => 'true',
44
+ :unchecked_value => 'false',
45
+ :checked => @deployment.neutron.cisco_nexus_mechanism?,
46
+ :text => _(Staypuft::Deployment::NeutronService::Ml2Mechanisms::LABELS['cisco_nexus']))
47
+ %>
48
+ </div>
49
+
50
+ <div class="neutron_n1kv_parameters inset_form hide">
51
+ <%= change_label_width 4, text_f(p, :n1kv_vsm_ip, class: "neutron_n1kv_vsm_ip", label: _('VSM IP')) %>
52
+ <%= change_label_width 4, text_f(p, :n1kv_vsm_password, class: "neutron_n1kv_vsm_password", label: _('VSM Password')) %>
41
53
  </div>
54
+
42
55
  <div class="neutron_cisco_nexus col-md-offset-1 hide">
43
56
  <div id="nexuses" class="neutron_nexus_picker">
44
57
  <% @deployment.neutron.nexuses.each_with_index do |nexus, index| %>
@@ -55,6 +55,8 @@
55
55
  :step => Staypuft::Deployment::STEP_SETTINGS } %>
56
56
 
57
57
  <% end %>
58
+
59
+ <%= textarea_f f, :custom_repos, :rows => 3, :help_inline => _('If you need to add custom repositories on provisioned hosts you can specify base urls here, one per line. These repositories will have highest priority (50)') %>
58
60
  <% end %>
59
61
  </div>
60
62
 
@@ -1,4 +1,5 @@
1
1
  <%= javascript 'staypuft/subnets_assignment' %>
2
+ <%= javascript 'staypuft/new_subnet' %>
2
3
 
3
4
  <%= render :layout => 'title' do %>
4
5
  <%= alert_if_deployed %>
@@ -25,7 +26,11 @@
25
26
  <div class="row">
26
27
  <div id="title_action" class="col-md-12">
27
28
  <div class="btn-toolbar pull-right">
28
- <%= display_link_if_authorized(_("New Subnet"), hash_for_new_subnet_path(:redirect => request.url + '#new')) %>
29
+ <button type="button" class= "btn btn-success"
30
+ id="new_subnet_button" data-toggle = "modal"
31
+ data-target = "#new_subnet_modal">
32
+ <%= _("New Subnet") %>
33
+ </button>
29
34
  </div>
30
35
  </div>
31
36
  </div>
@@ -57,4 +62,26 @@
57
62
  </div>
58
63
  </div>
59
64
  </div>
65
+
66
+ <div class="modal fade" id="new_subnet_modal" tabindex="-1" role="dialog" aria-labelledby="<%= _("New Subnet") %>" aria-hidden="true" data-path="<%= new_staypuft_subnet_path(:deployment_id => @deployment) %>">
67
+ <div class="modal-dialog modal-lg">
68
+ <div class="modal-content">
69
+ <div class="modal-header">
70
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
71
+ <h2 class="modal-title">
72
+ <%= _("New Subnet") %>
73
+ </h2>
74
+ </div>
75
+ <div id="new_subnet_ajax_content">
76
+ <div class="modal-body">
77
+ <div id="new_subnet_form"><%= image_tag '/assets/spinner.gif', style: "display: block; margin-left: auto; margin-right: auto" %></div>
78
+ </div>
79
+ <div class="modal-footer">
80
+ <button type="button" class="btn btn-default" data-dismiss="modal"><%= _("Cancel") %></button>
81
+ <button id="create_subnet_btn" type="button" class="btn btn-primary done" data-dismiss="modal"><%= _("Done") %></button>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
60
87
  <% end %>
@@ -0,0 +1,24 @@
1
+ <%= form_for(subnet, :url => staypuft_subnets_path(:deployment_id => deployment), :remote => true, :html => {:id => "new_subnet_form", :class => ""}) do |f| %>
2
+ <div class="modal-body">
3
+ <%= change_label_width(4, text_f(f, :name, :label => _("Name"))) %>
4
+ <%= change_label_width(4, select_f(f, :dhcp_server,
5
+ ::Staypuft::SimpleSubnet::DHCP_SERVER_TYPE,
6
+ :first, :last,
7
+ { include_blank: false },
8
+ { label: _("DHCP server") })) %>
9
+ <%= change_label_width(4, text_f(f, :network_address, :label => _("Network Address"),
10
+ :help_inline => _("Network Address using CIDR notation (eg. 1.2.3.4/24)"))) %>
11
+ <%= change_label_width(4, text_f(f, :vlan, :label => _("VLAN"))) %>
12
+ <div id="no_existing_dhcp_fields" style="display: <%= subnet.dhcp_server == 'none' ? 'block' : 'none' %>">
13
+ <%= change_label_width(4, text_f(f, :gateway, :label => _("Gateway"))) %>
14
+ <%= change_label_width(4, text_f(f, :ip_range_from, :label => _("IP Range Start"))) %>
15
+ <%= change_label_width(4, text_f(f, :ip_range_to, :label => _("IP Range End"))) %>
16
+ </div>
17
+ </div>
18
+ <div class="modal-footer">
19
+ <button type="button" class="btn btn-default" data-dismiss="modal"><%= _("Cancel") %></button>
20
+ <%= submit_tag _("Create Subnet"),
21
+ :id => "create_subnet_btn",
22
+ :class => "btn btn-primary done" %>
23
+ </div>
24
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <% if @result %>
2
+ $("#subnets").append(
3
+ '<%=j render 'staypuft/subnets/drop_zone',
4
+ :subnet => @simple_subnet.subnet,
5
+ :deployment => @deployment %>');
6
+ $('#new_subnet_modal').modal('toggle');
7
+ subnets_assigments();
8
+ <% else %>
9
+ $("#new_subnet_ajax_content").html(
10
+ '<%=j render 'staypuft/subnets/form',
11
+ :deployment => @deployment,
12
+ :subnet => @simple_subnet %>');
13
+ new_subnet();
14
+ <% end %>