staypuft 0.1.5 → 0.1.6

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 (45) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/staypuft/staypuft.js +99 -1
  3. data/app/assets/stylesheets/staypuft/bootstrap_and_overrides.css.scss +48 -4
  4. data/app/assets/stylesheets/staypuft/staypuft.css.scss +7 -11
  5. data/app/controllers/staypuft/deployments_controller.rb +23 -20
  6. data/app/controllers/staypuft/steps_controller.rb +18 -24
  7. data/app/helpers/staypuft/application_helper.rb +16 -1
  8. data/app/helpers/staypuft/deployments_helper.rb +1 -1
  9. data/app/lib/actions/staypuft/host/wait_until_host_ready.rb +2 -8
  10. data/app/lib/staypuft/seeder.rb +702 -0
  11. data/app/models/staypuft/concerns/host_open_stack_affiliation.rb +29 -2
  12. data/app/models/staypuft/concerns/hostgroup_extensions.rb +28 -3
  13. data/app/models/staypuft/concerns/lookup_key_extensions.rb +72 -0
  14. data/app/models/staypuft/deployment/abstract_param_scope.rb +31 -0
  15. data/app/models/staypuft/deployment/attribute_param_storage.rb +41 -0
  16. data/app/models/staypuft/deployment/cinder_service.rb +87 -0
  17. data/app/models/staypuft/deployment/glance_service.rb +85 -0
  18. data/app/models/staypuft/deployment/ips.rb +26 -0
  19. data/app/models/staypuft/deployment/neutron_service.rb +165 -0
  20. data/app/models/staypuft/deployment/nova_service.rb +84 -0
  21. data/app/models/staypuft/deployment/passwords.rb +66 -0
  22. data/app/models/staypuft/deployment/vips.rb +36 -0
  23. data/app/models/staypuft/deployment.rb +179 -72
  24. data/app/models/staypuft/deployment_role_hostgroup.rb +1 -1
  25. data/app/models/staypuft/role.rb +8 -1
  26. data/app/models/staypuft/service/ui_params.rb +12 -1
  27. data/app/views/staypuft/deployments/_assigned_hosts_table.html.erb +60 -0
  28. data/app/views/staypuft/deployments/_deployed_hosts_table.html.erb +51 -0
  29. data/app/views/staypuft/deployments/_free_hosts_table.html.erb +47 -0
  30. data/app/views/staypuft/deployments/edit.html.erb +50 -0
  31. data/app/views/staypuft/deployments/show.html.erb +41 -79
  32. data/app/views/staypuft/deployments/summary.html.erb +4 -1
  33. data/app/views/staypuft/steps/_cinder.html.erb +17 -0
  34. data/app/views/staypuft/steps/_glance.html.erb +16 -0
  35. data/app/views/staypuft/steps/_neutron.html.erb +57 -0
  36. data/app/views/staypuft/steps/_nova.html.erb +34 -0
  37. data/app/views/staypuft/steps/deployment_settings.html.erb +41 -17
  38. data/app/views/staypuft/steps/services_configuration.html.erb +19 -32
  39. data/app/views/staypuft/steps/{services_selection.html.erb → services_overview.html.erb} +7 -3
  40. data/config/routes.rb +2 -0
  41. data/db/migrate/20140623142500_remove_amqp_provider_from_staypuft_deployment.rb +6 -0
  42. data/db/seeds.rb +1 -314
  43. data/lib/staypuft/engine.rb +1 -0
  44. data/lib/staypuft/version.rb +1 -1
  45. metadata +23 -3
@@ -0,0 +1,66 @@
1
+ module Staypuft
2
+ class Deployment::Passwords < Deployment::AbstractParamScope
3
+ PASSWORD_LIST = :admin, :ceilometer_user, :cinder_db, :cinder_user,
4
+ :glance_db, :glance_user, :heat_db, :heat_user, :heat_cfn_user, :mysql_root,
5
+ :keystone_db, :keystone_user, :neutron_db, :neutron_user, :nova_db, :nova_user,
6
+ :swift_admin, :swift_user, :amqp, :amqp_nssdb, :keystone_admin_token,
7
+ :ceilometer_metering_secret, :heat_auth_encrypt_key, :horizon_secret_key,
8
+ :swift_shared_secret, :neutron_metadata_proxy_secret
9
+
10
+ OTHER_ATTRS_LIST = :mode, :single_password
11
+
12
+ def self.param_scope
13
+ 'passwords'
14
+ end
15
+
16
+ param_attr *OTHER_ATTRS_LIST, *PASSWORD_LIST
17
+
18
+ def initialize(deployment)
19
+ super deployment
20
+ self.single_password_confirmation = single_password
21
+ end
22
+
23
+ module Mode
24
+ SINGLE = 'single'
25
+ RANDOM = 'random'
26
+ LABELS = { RANDOM => N_('Generate random password for each service'),
27
+ SINGLE => N_('Use single password for all services') }
28
+ TYPES = LABELS.keys
29
+ HUMAN = N_('Service Password')
30
+ end
31
+
32
+ validates :mode, presence: true, inclusion: { in: Mode::TYPES }
33
+
34
+ # using old hash syntax here since if:, while validly parsing as :if => in
35
+ # ruby itself, in irb the parser treats it as an if keyword, as does both
36
+ # emacs and rubymine, which really messes with indention, etc.
37
+ validates :single_password,
38
+ :presence => true,
39
+ :confirmation => true,
40
+ :if => :single_mode?,
41
+ :length => { minimum: 6 }
42
+
43
+ def set_defaults
44
+ self.mode = Mode::RANDOM
45
+ PASSWORD_LIST.each do |password_field|
46
+ self.send("#{password_field}=", SecureRandom.hex)
47
+ end
48
+ end
49
+
50
+ def single_mode?
51
+ mode == Mode::SINGLE
52
+ end
53
+
54
+ def effective_value(password_field)
55
+ if single_mode?
56
+ single_password
57
+ else
58
+ send(password_field)
59
+ end
60
+ end
61
+
62
+ def id # compatibility with password_f
63
+ single_password
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,36 @@
1
+ module Staypuft
2
+ class Deployment::VIPS < Deployment::AbstractParamScope
3
+
4
+ VIP_NAMES = [:ceilometer, :cinder, :db, :glance, :heat, :horizon, :keystone, :loadbalancer,
5
+ :nova, :neutron, :amqp, :swift]
6
+ COUNT = VIP_NAMES.size
7
+
8
+ def self.param_scope
9
+ 'vips'
10
+ end
11
+
12
+ param_attr :user_range
13
+
14
+ HUMAN = N_('Virtual IP addresses range')
15
+
16
+ def range
17
+ (user_range || default_range)
18
+ # TODO reserve the IP addresses
19
+ end
20
+
21
+ # TODO validate range that it is array with size 11 and that it contains only IPS
22
+
23
+ def default_range
24
+ @default_range ||= begin
25
+ top_ip = hostgroup.subnet.to.split('.').map &:to_i
26
+ ((top_ip[-1]-(COUNT-1))..top_ip[-1]).
27
+ map { |last| [*top_ip[0..2], last] }.
28
+ map { |ip| ip.join '.' }
29
+ end
30
+ end
31
+
32
+ def get(name)
33
+ range[VIP_NAMES.index(name) || raise(ArgumentError, "unknown #{name}")]
34
+ end
35
+ end
36
+ end
@@ -6,22 +6,19 @@ module Staypuft
6
6
  STEP_SETTINGS = :settings
7
7
  STEP_CONFIGURATION = :configuration
8
8
  STEP_COMPLETE = :complete
9
- STEP_SELECTION = :selection
9
+ STEP_OVERVIEW = :overview
10
10
 
11
- NEW_NAME_PREFIX="uninitialized_"
11
+ NEW_NAME_PREFIX = 'uninitialized_'
12
12
 
13
- # amqp providers
14
- AMQP_RABBITMQ = "rabbitmq"
15
- AMQP_QPID = "qpid"
16
- AMQP_PROVIDERS = [AMQP_RABBITMQ, AMQP_QPID]
17
- AMQP_PROVIDER_LABELS = {AMQP_RABBITMQ => "RabbitMQ",
18
- AMQP_QPID => "Qpid" }
19
-
20
- attr_accessible :description, :name, :layout_id, :layout, :amqp_provider
13
+ attr_accessible :description, :name, :layout_id, :layout,
14
+ :amqp_provider, :layout_name, :networking, :platform
21
15
  after_save :update_hostgroup_name
22
16
  after_validation :check_form_complete
23
17
 
24
18
  belongs_to :layout
19
+
20
+ # needs to be defined before hostgroup association
21
+ before_destroy :prepare_destroy
25
22
  belongs_to :hostgroup, :dependent => :destroy
26
23
 
27
24
  has_many :deployment_role_hostgroups, :dependent => :destroy
@@ -45,80 +42,123 @@ module Staypuft
45
42
  validates :layout, :presence => true
46
43
  validates :hostgroup, :presence => true
47
44
 
48
- validates :amqp_provider, :presence => true, :inclusion => {:in => AMQP_PROVIDERS }
45
+ after_validation :check_form_complete
46
+ before_save :update_layout
47
+ after_save :update_based_on_settings
49
48
 
50
- # TODO(mtaylor)
51
- # Use conditional validations to validate the deployment multi-step form.
52
- # deployment.form_step should be used to check the form step the user is
53
- # currently on.
54
- # e.g.
55
- # validates :name, :presence => true, :if => :form_step_is_configuation?
49
+ SCOPES = [[:nova, :@nova_service, NovaService],
50
+ [:neutron, :@neutron_service, NeutronService],
51
+ [:glance, :@glance_service, GlanceService],
52
+ [:cinder, :@cinder_service, CinderService],
53
+ [:passwords, :@passwords, Passwords],
54
+ [:vips, :@vips, VIPS],
55
+ [:ips, :@ips, IPS]]
56
56
 
57
- scoped_search :on => :name, :complete_value => :true
57
+ SCOPES.each do |name, ivar, scope_class|
58
+ define_method name do
59
+ instance_variable_get ivar or
60
+ instance_variable_set ivar, scope_class.new(self)
61
+ end
62
+ after_save { send(name).run_callbacks :save }
63
+ end
58
64
 
59
- def self.available_locks
60
- [:deploy]
65
+ validates_associated :nova, :if => lambda { |d| d.form_step_is_past_configuration? && d.nova.active? }
66
+ validates_associated :neutron, :if => lambda { |d| d.form_step_is_past_configuration? && d.neutron.active? }
67
+ validates_associated :glance, :if => lambda {|d| d.form_step_is_past_configuration? && d.glance.active? }
68
+ validates_associated :cinder, :if => lambda {|d| d.form_step_is_past_configuration? && d.cinder.active? }
69
+ validates_associated :passwords
70
+
71
+ def initialize(attributes = {}, options = {})
72
+ super({ amqp_provider: AmqpProvider::RABBITMQ,
73
+ layout_name: LayoutName::NON_HA,
74
+ networking: Networking::NOVA,
75
+ platform: Platform::RHEL7 }.merge(attributes),
76
+ options)
77
+
78
+ self.hostgroup = Hostgroup.new(name: name, parent: Hostgroup.get_base_hostgroup)
79
+
80
+ self.nova.set_defaults
81
+ self.neutron.set_defaults
82
+ self.glance.set_defaults
83
+ self.cinder.set_defaults
84
+ self.passwords.set_defaults
85
+ self.layout = Layout.where(:name => self.layout_name,
86
+ :networking => self.networking).first
61
87
  end
62
88
 
63
- def destroy
64
- child_hostgroups.each do |h|
65
- h.destroy
66
- end
67
- #do the main destroy
68
- super
89
+ extend AttributeParamStorage
90
+
91
+ # Returns a list of hosts that are currently being deployed.
92
+ def in_progress_hosts(hostgroup)
93
+ return in_progress? ? hostgroup.openstack_hosts : {}
69
94
  end
70
95
 
71
- # After setting or changing layout, update the set of child hostgroups,
72
- # adding groups for any roles not already represented, and removing others
73
- # no longer needed.
74
- def update_hostgroup_list
75
- old_role_hostgroups_arr = deployment_role_hostgroups.to_a
76
- layout.layout_roles.each do |layout_role|
77
- role_hostgroup = deployment_role_hostgroups.where(:role_id => layout_role.role).first_or_initialize do |drh|
78
- drh.hostgroup = Hostgroup.new(name: layout_role.role.name, parent: hostgroup)
79
- end
96
+ # Helper method for checking whether this deployment is in progress or not.
97
+ def in_progress?
98
+ ForemanTasks::Lock.locked? self, nil
99
+ end
80
100
 
81
- role_hostgroup.hostgroup.add_puppetclasses_from_resource(layout_role.role)
82
- layout_role.role.services.each do |service|
83
- role_hostgroup.hostgroup.add_puppetclasses_from_resource(service)
84
- end
85
- role_hostgroup.hostgroup.save!
101
+ # Returns all deployed hosts with no errors (default behaviour). Set
102
+ # errors=true to return all deployed hosts that have errors
103
+ def deployed_hosts(hostgroup, errors=false)
104
+ in_progress? ? {} : hostgroup.openstack_hosts(errors)
105
+ end
86
106
 
87
- role_hostgroup.deploy_order = layout_role.deploy_order
88
- role_hostgroup.save!
107
+ def self.param_scope
108
+ 'deployment'
109
+ end
89
110
 
90
- old_role_hostgroups_arr.delete(role_hostgroup)
91
- end
92
- # delete any prior mappings that remain
93
- old_role_hostgroups_arr.each do |role_hostgroup|
94
- role_hostgroup.hostgroup.destroy
95
- end
111
+ module AmqpProvider
112
+ RABBITMQ = 'rabbitmq'
113
+ QPID = 'qpid'
114
+ LABELS = { RABBITMQ => N_('RabbitMQ'), QPID => N_('Qpid') }
115
+ TYPES = LABELS.keys
116
+ HUMAN = N_('Messaging provider')
96
117
  end
97
118
 
98
- # If layout networking is set to 'neutron', then set include_neutron and
99
- # neutron on the hostgroup if it includes the "quickstack::pacemaker::params"
100
- # puppetclass
101
- def set_custom_params
102
- child_hostgroups.each do |the_hostgroup|
103
- the_hostgroup.puppetclasses.each do |pclass|
104
-
105
- # set params relating to neutron/nova networking choice
106
- if pclass.class_params.where(:key => "include_neutron").first
107
- the_hostgroup.set_param_value_if_changed(pclass, "include_neutron",
108
- layout.networking == 'neutron')
109
- end
110
- if pclass.class_params.where(:key => "neutron").first
111
- the_hostgroup.set_param_value_if_changed(pclass, "neutron",
112
- layout.networking == 'neutron')
113
- end
114
-
115
- # set params relating to rabbitmq/qpid amqp choice
116
- if pclass.class_params.where(:key => "amqp_server").first
117
- the_hostgroup.set_param_value_if_changed(pclass, "amqp_server",
118
- amqp_provider)
119
- end
120
- end
121
- end
119
+ module Networking
120
+ NOVA = 'nova'
121
+ NEUTRON = 'neutron'
122
+ LABELS = { NOVA => N_('Nova Network'), NEUTRON => N_('Neutron Networking') }
123
+ TYPES = LABELS.keys
124
+ HUMAN = N_('Networking')
125
+ end
126
+
127
+ module LayoutName
128
+ NON_HA = 'Controller / Compute'
129
+ HA = 'High Availability Controllers / Compute'
130
+ LABELS = { NON_HA => N_('Controller / Compute'),
131
+ HA => N_('High Availability Controllers / Compute') }
132
+ TYPES = LABELS.keys
133
+ HUMAN = N_('High Availability')
134
+ end
135
+
136
+ module Platform
137
+ RHEL7 = 'rhel7'
138
+ RHEL6 = 'rhel6'
139
+ LABELS = { RHEL7 => N_('Red Hat Enterprise Linux OpenStack Platform 5 with RHEL 7'),
140
+ RHEL6 => N_('Red Hat Enterprise Linux OpenStack Platform 5 with RHEL 6') }
141
+ TYPES = LABELS.keys
142
+ HUMAN = N_('Platform')
143
+ end
144
+
145
+ param_attr :amqp_provider, :networking, :layout_name, :platform
146
+ validates :amqp_provider, :presence => true, :inclusion => { :in => AmqpProvider::TYPES }
147
+ validates :networking, :presence => true, :inclusion => { :in => Networking::TYPES }
148
+ validates :layout_name, presence: true, inclusion: { in: LayoutName::TYPES }
149
+ validates :platform, presence: true, inclusion: { in: Platform::TYPES }
150
+
151
+ # TODO(mtaylor)
152
+ # Use conditional validations to validate the deployment multi-step form.
153
+ # deployment.form_step should be used to check the form step the user is
154
+ # currently on.
155
+ # e.g.
156
+ # validates :name, :presence => true, :if => :form_step_is_configuration?
157
+
158
+ scoped_search :on => :name, :complete_value => :true
159
+
160
+ def self.available_locks
161
+ [:deploy]
122
162
  end
123
163
 
124
164
  def services_hostgroup_map
@@ -133,20 +173,87 @@ module Staypuft
133
173
  self.hosts.any?(&:open_stack_deployed?)
134
174
  end
135
175
 
176
+ def form_step_is_configuration?
177
+ self.form_step.to_sym == Deployment::STEP_CONFIGURATION
178
+ end
179
+
180
+ def form_step_is_past_configuration?
181
+ self.form_step_is_configuration? || self.form_complete?
182
+ end
183
+
136
184
  def form_complete?
137
185
  self.form_step.to_sym == Deployment::STEP_COMPLETE
138
186
  end
139
187
 
188
+ def ha?
189
+ self.layout_name == LayoutName::HA
190
+ end
191
+
140
192
  private
193
+
194
+ def update_layout
195
+ self.layout = Layout.where(:name => layout_name, :networking => networking).first
196
+ end
197
+
198
+ def update_based_on_settings
199
+ update_hostgroup_name
200
+ update_operating_system
201
+ update_hostgroup_list
202
+ end
203
+
141
204
  def update_hostgroup_name
142
205
  hostgroup.name = self.name
143
206
  hostgroup.save!
144
207
  end
145
208
 
209
+ def update_operating_system
210
+ self.hostgroup.operatingsystem = case platform
211
+ when Platform::RHEL6
212
+ Operatingsystem.where(name: 'RedHat', major: '6', minor: '5').first
213
+ when Platform::RHEL7
214
+ Operatingsystem.where(name: 'RedHat', major: '7', minor: '0').first
215
+ Operatingsystem.where(name: 'RedHat', major: '6', minor: '5').first
216
+ end or
217
+ raise 'missing Operatingsystem'
218
+ self.hostgroup.save!
219
+ end
220
+
221
+ # After setting or changing layout, update the set of child hostgroups,
222
+ # adding groups for any roles not already represented, and removing others
223
+ # no longer needed.
224
+ def update_hostgroup_list
225
+ old_deployment_role_hostgroups = deployment_role_hostgroups.to_a
226
+ new_deployment_role_hostgroups = layout.layout_roles.map do |layout_role|
227
+ deployment_role_hostgroup = deployment_role_hostgroups.where(:role_id => layout_role.role).first_or_initialize do |drh|
228
+ drh.hostgroup = Hostgroup.new(name: layout_role.role.name, parent: hostgroup)
229
+ end
230
+
231
+ deployment_role_hostgroup.hostgroup.add_puppetclasses_from_resource(layout_role.role)
232
+ layout_role.role.services.each do |service|
233
+ deployment_role_hostgroup.hostgroup.add_puppetclasses_from_resource(service)
234
+ end
235
+ # deployment_role_hostgroup.hostgroup.save!
236
+
237
+ deployment_role_hostgroup.deploy_order = layout_role.deploy_order
238
+ deployment_role_hostgroup.save!
239
+
240
+ deployment_role_hostgroup
241
+ end
242
+
243
+ # delete any prior mappings that remain
244
+ (old_deployment_role_hostgroups - new_deployment_role_hostgroups).each &:destroy
245
+ end
246
+
146
247
  # Checks to see if the form step was the last in the series. If so it sets
147
248
  # the form_step field to complete.
148
249
  def check_form_complete
149
250
  self.form_step = Deployment::STEP_COMPLETE if self.form_step.to_sym == Deployment::STEP_CONFIGURATION
150
251
  end
252
+
253
+ def prepare_destroy
254
+ hosts.each &:open_stack_unassign
255
+ child_hostgroups.each &:destroy
256
+ end
257
+
151
258
  end
152
259
  end
@@ -3,7 +3,7 @@ module Staypuft
3
3
  attr_accessible :deployment, :deployment_id, :hostgroup, :hostgroup_id, :role, :role_id, :deploy_order
4
4
 
5
5
  belongs_to :deployment
6
- belongs_to :hostgroup
6
+ belongs_to :hostgroup, dependent: :destroy
7
7
  belongs_to :role
8
8
  has_many :services, :through => :role
9
9
 
@@ -15,6 +15,13 @@ module Staypuft
15
15
 
16
16
  attr_accessible :description, :max_hosts, :min_hosts, :name
17
17
 
18
- validates :name, :presence => true, :uniqueness => true
18
+ validates :name, :presence => true, :uniqueness => true
19
+
20
+ scope(:in_deployment, lambda do |deployment|
21
+ joins(:deployment_role_hostgroups).
22
+ where(DeploymentRoleHostgroup.table_name => { deployment_id: deployment })
23
+ end)
24
+
25
+ scope(:controller, where(name: Seeder::CONTROLLER_ROLES.map { |h| h.fetch(:name) }))
19
26
  end
20
27
  end
@@ -83,7 +83,10 @@ Staypuft::Service::UI_PARAMS = {
83
83
  'swift_storage_device',
84
84
  'swift_storage_ips'],
85
85
  'Glance (non-HA)' => ['glance_db_password',
86
- 'glance_user_password'],
86
+ 'glance_user_password',
87
+ 'glance_backend',
88
+ 'glance_rbd_store_user',
89
+ 'glance_rbd_store_pool'],
87
90
  'Cinder (controller)' => ['cinder_backend_eqlx',
88
91
  'cinder_backend_eqlx_name',
89
92
  'cinder_backend_gluster',
@@ -92,6 +95,8 @@ Staypuft::Service::UI_PARAMS = {
92
95
  'cinder_backend_iscsi_name',
93
96
  'cinder_backend_nfs',
94
97
  'cinder_backend_nfs_name',
98
+ 'cinder_backend_rbd',
99
+ 'cinder_backend_rbd_name',
95
100
  'cinder_db_password',
96
101
  'cinder_multiple_backends',
97
102
  'cinder_gluster_shares',
@@ -106,6 +111,12 @@ Staypuft::Service::UI_PARAMS = {
106
111
  'cinder_eqlx_use_chap',
107
112
  'cinder_eqlx_chap_login',
108
113
  'cinder_eqlx_chap_password',
114
+ 'cinder_rbd_pool',
115
+ 'cinder_rbd_ceph_conf',
116
+ 'cinder_rbd_flatten_volume_from_snapshot',
117
+ 'cinder_rbd_max_clone_depth',
118
+ 'cinder_rbd_user',
119
+ 'cinder_rbd_secret_uuid',
109
120
  'cinder_user_password'],
110
121
  'Heat' => ['heat_cfn',
111
122
  'heat_cloudwatch',
@@ -0,0 +1,60 @@
1
+ <div class="tab-pane" id="<%= child_hostgroup.name.parameterize.underscore %>_assigned_hosts">
2
+ <% if child_hostgroup.hosts.present? %>
3
+ <%= form_tag(unassign_host_deployment_path(id: deployment), class: 'form-horizontal well association') do |f| %>
4
+ <%= submit_tag _("Unassign Hosts"), :class => "btn btn-primary btn-sm pull-left" %>
5
+ <h4 class="pull-left"><%= _("Assigned Hosts") %></h4>
6
+ <button type="button" class="close" aria-hidden="true">×</button>
7
+ <p class="clearfix"></p>
8
+ <%= hidden_field_tag :hostgroup_id, child_hostgroup.id %>
9
+ <table class="table table-bordered table-striped table-condensed">
10
+ <thead>
11
+ <tr>
12
+ <th class="ca">
13
+ <%= check_box_tag :check_all %>
14
+ </th>
15
+ <th><%= sort :name, :as => _('Name') %></th>
16
+ <th class="hidden-s hidden-xs"><%= _('Deploying?') %></th>
17
+ <th class="hidden-s hidden-xs"><%= sort :ip, :as => _('IP Address') %></th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <% child_hostgroup.hosts.each do |host| %>
22
+ <% disabled = ForemanTasks::Lock.locked?(deployment, nil) && host.open_stack_deployed? %>
23
+ <tr class="<%= ['checkbox_highlight',
24
+ ('deploying' if disabled)
25
+ ].compact.join(' ') %>">
26
+ <td class="ca">
27
+ <%= check_box_tag 'host_ids[]',
28
+ host.id,
29
+ false,
30
+ id: "host_ids_#{host.id}",
31
+ disabled: disabled %>
32
+ <%= hidden_field_tag 'host_ids[]', host.id if disabled %>
33
+ </td>
34
+ <td class="ellipsis">
35
+ <%= host_label(host) %>
36
+ </td>
37
+ <td class="hidden-s hidden-xs">
38
+ <% if disabled %>
39
+ <span class="glyphicon glyphicon-cloud-upload"></span>
40
+ <% else %>
41
+ <span class="glyphicon glyphicon-minus"></span>
42
+ <% end %>
43
+ </td>
44
+ <td class="hidden-s hidden-xs"><%= host.ip %></td>
45
+ </tr>
46
+ <% end %>
47
+ </tbody>
48
+ </table>
49
+ <% end %>
50
+ <% else %>
51
+ <div class="well association">
52
+ <button type="button" class="close pull-right" aria-hidden="true">×</button>
53
+ <h4><%= _("Assigned Hosts") %></h4>
54
+ <div class="alert alert-warning">
55
+ <span class="glyphicon glyphicon-warning-sign">&nbsp;</span>
56
+ <%= _("No hosts were assigned to this group yet.") %>
57
+ </div>
58
+ </div>
59
+ <% end %>
60
+ </div>
@@ -0,0 +1,51 @@
1
+ <div class="tab-pane" id="<%= child_hostgroup.name.parameterize.underscore %>_deployed_hosts">
2
+ <% if child_hostgroup.hosts.select { |h| !ForemanTasks::Lock.locked?(deployment, nil) && h.open_stack_deployed? }.present? %>
3
+ <%= form_tag(unassign_host_deployment_path(id: deployment), class: 'form-horizontal well association') do |f| %>
4
+ <%= submit_tag _("Unassign Hosts"),
5
+ :class => "btn btn-danger btn-sm pull-left",
6
+ :'data-toggle' => "tooltip",
7
+ :'data-placement' => "right",
8
+ :title => _("Unassigning deployed hosts may lead to unexpected problems.") %>
9
+ <h4 class="pull-left"><%= _("Deployed Hosts") %></h4>
10
+ <button type="button" class="close" aria-hidden="true">×</button>
11
+ <p class="clearfix"></p>
12
+ <%= hidden_field_tag :hostgroup_id, child_hostgroup.id %>
13
+ <table class="table table-bordered table-striped table-condensed">
14
+ <thead>
15
+ <tr>
16
+ <th class="ca">
17
+ <%= check_box_tag :check_all %>
18
+ </th>
19
+ <th><%= sort :name, :as => _('Name') %></th>
20
+ <th class="hidden-s hidden-xs"><%= sort :ip, :as => _('IP Address') %></th>
21
+ </tr>
22
+ </thead>
23
+ <tbody>
24
+ <% child_hostgroup.hosts.select{ |h| !ForemanTasks::Lock.locked?(deployment, nil) && h.open_stack_deployed? }.each do |host| %>
25
+ <tr class="checkbox_highlight deployed">
26
+ <td class="ca">
27
+ <%= check_box_tag 'host_ids[]',
28
+ host.id,
29
+ false,
30
+ id: "host_ids_#{host.id}" %>
31
+ </td>
32
+ <td class="ellipsis">
33
+ <%= host_label(host) %>
34
+ </td>
35
+ <td class="hidden-s hidden-xs"><%= host.ip %></td>
36
+ </tr>
37
+ <% end %>
38
+ </tbody>
39
+ </table>
40
+ <% end %>
41
+ <% else %>
42
+ <div class="well association">
43
+ <button type="button" class="close pull-right" aria-hidden="true">×</button>
44
+ <h4><%= _("Deployed Hosts") %></h4>
45
+ <div class="alert alert-warning">
46
+ <span class="glyphicon glyphicon-warning-sign">&nbsp;</span>
47
+ <%= _("No hosts from this group have been deployed yet.") %>
48
+ </div>
49
+ </div>
50
+ <% end %>
51
+ </div>
@@ -0,0 +1,47 @@
1
+ <div class="tab-pane" id="<%= child_hostgroup.name.parameterize.underscore %>_free_hosts">
2
+ <% if child_hostgroup.free_hosts.present? %>
3
+ <%= form_tag(associate_host_deployment_path(id: deployment), class: 'form-horizontal well association') do |f| %>
4
+ <%= submit_tag _("Assign Hosts"), :class => "btn btn-primary btn-sm pull-left" %>
5
+ <h4 class="pull-left"><%= _("Free Hosts") %></h4>
6
+ <button type="button" class="close" aria-hidden="true">×</button>
7
+ <p class="clearfix"></p>
8
+ <%= hidden_field_tag :hostgroup_id, child_hostgroup.id %>
9
+ <table class="table table-bordered table-striped table-condensed">
10
+ <thead>
11
+ <tr>
12
+ <th class="ca">
13
+ <%= check_box_tag :check_all %>
14
+ </th>
15
+ <th><%= sort :name, :as => _('Name') %></th>
16
+ <th class="hidden-s hidden-xs"><%= sort :ip, :as => _('IP Address') %></th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ <% child_hostgroup.free_hosts.each do |host| %>
21
+ <tr class="checkbox_highlight">
22
+ <td class="ca">
23
+ <%= check_box_tag 'host_ids[]',
24
+ host.id,
25
+ child_hostgroup.host_ids.include?(host.id),
26
+ id: "host_ids_#{host.id}" %>
27
+ </td>
28
+ <td class="ellipsis">
29
+ <%= host_label(host) %>
30
+ </td>
31
+ <td class="hidden-s hidden-xs"><%= host.ip %></td>
32
+ </tr>
33
+ <% end %>
34
+ </tbody>
35
+ </table>
36
+ <% end %>
37
+ <% else %>
38
+ <div class="well association">
39
+ <button type="button" class="close pull-right" aria-hidden="true">×</button>
40
+ <h4><%= _("Free Hosts") %></h4>
41
+ <div class="alert alert-warning">
42
+ <span class="glyphicon glyphicon-warning-sign">&nbsp;</span>
43
+ <%= _("All available hosts have been already assigned.") %>
44
+ </div>
45
+ </div>
46
+ <% end %>
47
+ </div>