Class: Ironfan::Provider::OpenStack::Machine

Inherits:
IaasProvider::Machine show all
Defined in:
lib/ironfan/headers.rb,
lib/ironfan/provider/openstack/machine.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from IaasProvider::Machine

cloud_init_user_data

Class Method Details

.block_device_mapping(computer) ⇒ Object

An array of hashes with dorky-looking keys, just like Fog wants it.



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ironfan/provider/openstack/machine.rb', line 316

def self.block_device_mapping(computer)
  Ironfan.todo "CODE SMELL: Machine is too familiar with EbsVolume problems"
  computer.drives.values.map do |drive|
    next if drive.disk  # Don't create any disc already satisfied
    volume = drive.volume or next
    hsh = { 'DeviceName' => volume.device }
    if volume.attachable == 'ephemeral'
      hsh['VirtualName'] = drive.name
    # if set for creation at launch (and not already created)
    elsif drive.node[:volume_id].blank? && volume.create_at_launch
      if volume.snapshot_id.blank? && volume.size.blank?
        raise "Must specify a size or a snapshot ID for #{volume}"
      end
      hsh['Ebs.SnapshotId'] = volume.snapshot_id if volume.snapshot_id.present?
      hsh['Ebs.VolumeSize'] = volume.size.to_s   if volume.size.present?
      hsh['Ebs.DeleteOnTermination'] = (not volume.keep).to_s
    else next
    end
    hsh
  end.compact
end

.create!(computer) ⇒ Object

Manipulation



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/ironfan/provider/openstack/machine.rb', line 198

def self.create!(computer)
  Ironfan.todo("CODE SMELL: overly large method: #{caller}")
  return if computer.machine? and computer.machine.created?
  Ironfan.step(computer.name,"creating cloud machine", :green)
  #
  errors = lint(computer)
  if errors.present? then raise ArgumentError, "Failed validation: #{errors.inspect}" ; end
  #
  launch_desc = launch_description(computer)
  Chef::Log.debug(JSON.pretty_generate(launch_desc))

  # tag the computer correctly
  tags = {
    'cluster' =>      computer.server.cluster_name,
    'facet' =>        computer.server.facet_name,
    'index' =>        computer.server.index.to_s,
    'name' =>         computer.name,
    'creator' =>      Chef::Config.username
  }

  Ironfan.safely do
    fog_server = OpenStack.connection.servers.create(launch_desc)
    machine = Machine.new(:adaptee => fog_server)
    computer.machine = machine
    remember machine, :id => computer.name

    Ironfan.step(fog_server.id,"waiting for machine to be ready", :gray)
    Ironfan.tell_you_thrice     :name           => fog_server.id,
                                :problem        => "server unavailable",
                                :error_class    => Fog::Errors::Error do
      fog_server.wait_for { state == "ACTIVE" }
    end
  end

  
  computer.machine..set(tags)

  #OpenStack.ensure_tags(tags, computer.machine)

  # no volumes at the momnt

  # register the new volumes for later save!, and tag appropriately
  #computer.machine.volumes.each do |v|
  #  Ironfan.todo "CODE SMELL: Machine is too familiar with EbsVolume problems"
  #  ebs_vol = OpenStack::EbsVolume.register v
  #  drive = computer.drives.values.select do |drive|
  #    drive.volume.device == ebs_vol.device
  #  end.first
  #  drive.disk = ebs_vol
  # 
  #  vol_name = "#{computer.name}-#{drive.volume.name}"
  #  tags['server'] = computer.name
  #  tags['name'] = vol_name
  #  tags['Name'] = vol_name
  #  tags['mount_point'] = drive.volume.mount_point
  #  tags['device'] = drive.volume.device
  #  OpenStack.ensure_tags(tags,ebs_vol)
  #end
end

.destroy!(computer) ⇒ Object



338
339
340
341
342
343
# File 'lib/ironfan/provider/openstack/machine.rb', line 338

def self.destroy!(computer)
  return unless computer.machine?
  forget computer.machine.name
  computer.machine.destroy
  computer.machine.reload            # show the node as shutting down
end

.expected_ids(computer) ⇒ Object



42
# File 'lib/ironfan/provider/openstack/machine.rb', line 42

def self.expected_ids(computer) [computer.server.full_name];   end

.launch_description(computer) ⇒ Object



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/ironfan/provider/openstack/machine.rb', line 275

def self.launch_description(computer)
  cloud = computer.server.cloud(:openstack)
  user_data = self.cloud_init_user_data(computer)

  # main machine info
  # note that Fog does not actually create tags when it creates a
  #  server; they and permanence are applied during sync
  description = {
    :image_ref             => cloud.image_id,
    :flavor_ref            => OpenStack.flavor_hash[cloud.flavor].id,
    #:vpc_id               => cloud.vpc,
    #:subnet_id            => cloud.subnet,
    :key_name             => cloud.ssh_key_name(computer),
    :user_data            => user_data,
    #:block_device_mapping => block_device_mapping(computer),
    :availability_zone    => cloud.default_availability_zone,
    #:monitoring           => cloud.monitoring,
    :name                 => computer.name,
  }

  description[:security_groups] = (computer.server.security_groups.keys + cloud.security_groups.keys).uniq

  #description[:iam_server_certificates] = cloud.iam_server_certificates.values.map do |cert|
  #  IamServerCertificate.recall(IamServerCertificate.full_name(computer, cert))
  #end.compact.map(&:name)

  #description[:elastic_load_balancers] = cloud.elastic_load_balancers.values.map do |elb|
  #  ElasticLoadBalancer.recall(ElasticLoadBalancer.full_name(computer, elb))
  #end.compact.map(&:name)

  #if cloud.flavor_info[:placement_groupable]
  #  ui.warn "1.3.1 and earlier versions of Fog don't correctly support placement groups, so your nodes will land willy-nilly. We're working on a fix"
  #  description[:placement] = { 'groupName' => cloud.placement_group.to_s }
  #end
  #if cloud.flavor_info[:ebs_optimizable]
  #  description[:ebs_optimized] = cloud.ebs_optimized
  #end
  description
end

.lint(computer) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/ironfan/provider/openstack/machine.rb', line 259

def self.lint(computer)
  cloud = computer.server.cloud(:openstack)
  info  = [computer.name, cloud.inspect]
  errors = {}
  server_errors = computer.server.lint
  errors["Unhappy Server"]      = server_errors   if server_errors.present?
  errors["No AMI found"]        = info            if cloud.image_id.blank?
  errors['Missing client']      = info            unless computer.client?
  errors['Missing private_key'] = computer.client unless computer.private_key
  #
  #all_asserted_regions = [OpenStack.connection.region, cloud.region, Chef::Config[:knife][:region], Ironfan.chef_config[:region]].compact.uniq
  #errors["mismatched region"] = all_asserted_regions unless all_asserted_regions.count == 1
  #
  errors
end

.load!(cluster = nil) ⇒ Object

Discovery



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/ironfan/provider/openstack/machine.rb', line 156

def self.load!(cluster=nil)
  OpenStack.connection.servers.each do |fs|
    machine = new(:adaptee => fs)
    if (not machine.created?)
      next unless Ironfan.chef_config[:include_terminated]
      remember machine, :append_id => "terminated:#{machine.id}"
    elsif recall? machine.name
      machine.bogus <<                 :duplicate_machines
      recall(machine.name).bogus <<    :duplicate_machines
      remember machine, :append_id => "duplicate:#{machine.id}"
    else # never seen it
      remember machine
    end
  end
end

.multiple?Boolean

Returns:

  • (Boolean)


39
# File 'lib/ironfan/provider/openstack/machine.rb', line 39

def self.multiple?()    false;  end

.resource_typeObject

def self.resource_type() Ironfan::IaasProvider::Machine; end



41
# File 'lib/ironfan/provider/openstack/machine.rb', line 41

def self.resource_type()        :machine;   end

.save!(computer) ⇒ Object



345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/ironfan/provider/openstack/machine.rb', line 345

def self.save!(computer)
  return unless computer.machine?
  # the EC2 API does not surface disable_api_termination as a value, so we
  # have to set it every time.
  permanent = computer.server.cloud(:openstack).permanent
  return unless computer.created?
  #Ironfan.step(computer.name, "setting termination flag #{permanent}", :blue)
  #Ironfan.unless_dry_run do
  #  Ironfan.safely do
  #    OpenStack.connection.modify_instance_attribute( computer.machine.id,
  #      {'DisableApiTermination.Value' => computer.permanent?, })
  #  end
  #end
end

.shared?Boolean

Returns:

  • (Boolean)


38
# File 'lib/ironfan/provider/openstack/machine.rb', line 38

def self.shared?()      false;  end

.validate_resources!(computers) ⇒ Object

Find active machines that haven’t matched, but should have,

make sure all bogus machines have a computer to attach to
for display purposes


180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/ironfan/provider/openstack/machine.rb', line 180

def self.validate_resources!(computers)
  recall.each_value do |machine|
    next unless machine.users.empty? and machine.name
    if computers.clusters.any?{ |comp| machine.name.match("^#{comp.name}-") }
      machine.bogus << :unexpected_machine
    end
    next unless machine.bogus?
    fake           = Ironfan::Broker::Computer.new
    fake[:machine] = machine
    fake.name      = machine.name
    machine.users << fake
    computers     << fake
  end
end

Instance Method Details

#created?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/ironfan/provider/openstack/machine.rb', line 78

def created?
  not ['HARD_DELETED', 'SOFT_DELETED', ].include? state
end

#created_atObject



53
54
55
# File 'lib/ironfan/provider/openstack/machine.rb', line 53

def created_at
  return @adaptee.created
end

#dns_nameObject



74
# File 'lib/ironfan/provider/openstack/machine.rb', line 74

def dns_name            ; public_ip_address ; end

#error?Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/ironfan/provider/openstack/machine.rb', line 95

def error?
  state == "ERROR"
end

#flavor_idObject



57
58
59
60
# File 'lib/ironfan/provider/openstack/machine.rb', line 57

def flavor_id
  # sometimes flavor comes back empty - especially right after the machine has been launched
  return flavor && flavor["id"]
end

#flavor_nameObject



62
63
64
65
# File 'lib/ironfan/provider/openstack/machine.rb', line 62

def flavor_name
  fl = OpenStack.flavor_id_hash[ flavor_id ]
  fl && fl.name 
end

#groupsObject



71
# File 'lib/ironfan/provider/openstack/machine.rb', line 71

def groups           ; Array(@adaptee.security_groups)   ;   end

#image_idObject



67
68
69
# File 'lib/ironfan/provider/openstack/machine.rb', line 67

def image_id
  return image[:id]
end

#keypairObject



76
# File 'lib/ironfan/provider/openstack/machine.rb', line 76

def keypair          ; key_pair ; end

#pending?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/ironfan/provider/openstack/machine.rb', line 81

def pending?
  state == "BUILD"
end

#perform_after_launch_tasks?Boolean

Returns:

  • (Boolean)


111
112
113
# File 'lib/ironfan/provider/openstack/machine.rb', line 111

def perform_after_launch_tasks?
  true
end

#private_ip_addressObject



140
141
142
# File 'lib/ironfan/provider/openstack/machine.rb', line 140

def private_ip_address
  adaptee.private_ip_address rescue nil
end

#public_hostnameObject



73
# File 'lib/ironfan/provider/openstack/machine.rb', line 73

def public_hostname    ; private_ip_address ; end

#public_ip_addressObject



144
145
146
# File 'lib/ironfan/provider/openstack/machine.rb', line 144

def public_ip_address
  adaptee.floating_ip_address rescue nil
end

#receive_adaptee(obj) ⇒ Object



172
173
174
175
# File 'lib/ironfan/provider/openstack/machine.rb', line 172

def receive_adaptee(obj)
  obj = OpenStack.connection.servers.new(obj) if obj.is_a?(Hash)
  super
end

#running?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/ironfan/provider/openstack/machine.rb', line 84

def running?
  state == "ACTIVE"
end

#ssh_keyObject



136
137
138
# File 'lib/ironfan/provider/openstack/machine.rb', line 136

def ssh_key
  keypair = cloud.keypair || computer.server.keypair_name
end

#startObject



99
100
101
102
103
# File 'lib/ironfan/provider/openstack/machine.rb', line 99

def start
  machine = self
  adaptee.start
  adaptee.wait_for{ machine.pending? or machine.running? or machine.error? }
end

#stopObject



105
106
107
108
109
# File 'lib/ironfan/provider/openstack/machine.rb', line 105

def stop
  machine = self
  adaptee.stop
  adaptee.wait_for{ machine.stopping? or machine.stopped? }
end

#stopped?Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/ironfan/provider/openstack/machine.rb', line 91

def stopped?
  state == "STOPPED"
end

#stopping?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/ironfan/provider/openstack/machine.rb', line 87

def stopping?
  state == "STOPPING"
end

#tagsObject



44
45
46
47
# File 'lib/ironfan/provider/openstack/machine.rb', line 44

def tags
  t = .to_hash.update({"Name" => @adaptee.name})
  return t.keys.inject({}) {|h,k| h[k]=t[k]; h[k.to_sym]=t[k]; h}
end

#to_display(style, values = {}) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ironfan/provider/openstack/machine.rb', line 115

def to_display(style,values={})
  # style == :minimal
  values["State"] =             (state || "unknown").to_sym
  values["MachineID"] =         id
  values["Public IP"] =         public_ip_address
  values["Private IP"] =        private_ip_address
  values["Created On"] =        created_at.to_date
  return values if style == :minimal

  # style == :default
  values["Flavor"] =            flavor_name
  values["AZ"] =                availability_zone
  return values if style == :default

  # style == :expanded
  values["Image"] =             image_id
  #values["Volumes"] =           volumes.map(&:id).join(', ')
  values["SSH Key"] =           key_name
  values
end

#to_sObject



148
149
150
151
# File 'lib/ironfan/provider/openstack/machine.rb', line 148

def to_s
  "<%-15s %-12s %-25s %-25s %-15s %-15s %-12s %-12s %s:%s>" % [
    self.class.handle, id, created_at, name, private_ip_address, public_ip_address, flavor_name, availability_zone, key_name, groups.join(',') ]
end

#vpc_idObject



49
50
51
# File 'lib/ironfan/provider/openstack/machine.rb', line 49

def vpc_id
  return nil
end