File: server.rb

package info (click to toggle)
ruby-fog-openstack 1.1.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,784 kB
  • sloc: ruby: 47,937; makefile: 5; sh: 4
file content (410 lines) | stat: -rw-r--r-- 12,936 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
require 'fog/compute/models/server'
require 'fog/openstack/compute/models/metadata'

module Fog
  module OpenStack
    class Compute
      class Server < Fog::Compute::Server
        identity :id
        attribute :instance_name, :aliases => 'OS-EXT-SRV-ATTR:instance_name'

        attribute :addresses
        attribute :flavor
        attribute :host_id, :aliases => 'hostId'
        attribute :image
        attribute :metadata
        attribute :links
        attribute :name

        # @!attribute [rw] personality
        # @note This attribute is only used for server creation. This field will be nil on subsequent retrievals.
        # @return [Hash] Hash containing data to inject into the file system of the cloud server instance during
        #                server creation.
        # @example To inject fog.txt into file system
        #   :personality => [{ :path => '/root/fog.txt',
        #                      :contents => Base64.encode64('Fog was here!')
        #                   }]
        # @see #create
        # @see http://docs.openstack.org/api/openstack-compute/2/content/Server_Personality-d1e2543.html
        attribute :personality
        attribute :progress
        attribute :accessIPv4
        attribute :accessIPv6
        attribute :availability_zone, :aliases => 'OS-EXT-AZ:availability_zone'
        attribute :user_data_encoded
        attribute :state,       :aliases => 'status'
        attribute :created,     :type => :time
        attribute :updated,     :type => :time

        attribute :tenant_id
        attribute :user_id
        attribute :key_name
        attribute :fault
        attribute :config_drive
        attribute :os_dcf_disk_config, :aliases => 'OS-DCF:diskConfig'
        attribute :os_ext_srv_attr_host, :aliases => 'OS-EXT-SRV-ATTR:host'
        attribute :os_ext_srv_attr_hypervisor_hostname, :aliases => 'OS-EXT-SRV-ATTR:hypervisor_hostname'
        attribute :os_ext_srv_attr_instance_name, :aliases => 'OS-EXT-SRV-ATTR:instance_name'
        attribute :os_ext_sts_power_state, :aliases => 'OS-EXT-STS:power_state'
        attribute :os_ext_sts_task_state, :aliases => 'OS-EXT-STS:task_state'
        attribute :os_ext_sts_vm_state, :aliases => 'OS-EXT-STS:vm_state'

        attr_reader :password
        attr_writer :image_ref, :flavor_ref, :nics, :os_scheduler_hints
        attr_accessor :block_device_mapping, :block_device_mapping_v2

        # In some cases it's handy to be able to store the project for the record, e.g. swift doesn't contain project
        # info in the result, so we can track it in this attribute based on what project was used in the request
        attr_accessor :project

        def initialize(attributes = {})
          # Old 'connection' is renamed as service and should be used instead
          prepare_service_value(attributes)

          self.security_groups = attributes.delete(:security_groups)
          self.min_count = attributes.delete(:min_count)
          self.max_count = attributes.delete(:max_count)
          self.nics = attributes.delete(:nics)
          self.os_scheduler_hints = attributes.delete(:os_scheduler_hints)
          self.block_device_mapping = attributes.delete(:block_device_mapping)
          self.block_device_mapping_v2 = attributes.delete(:block_device_mapping_v2)

          super
        end

        def metadata
          @metadata ||= begin
            Fog::OpenStack::Compute::Metadata.new(:service => service,
                                                  :parent  => self)
          end
        end

        def metadata=(new_metadata = {})
          return unless new_metadata
          metas = []
          new_metadata.each { |k, v| metas << {"key" => k, "value" => v} }
          @metadata = metadata.load(metas)
        end

        def user_data=(ascii_userdata)
          self.user_data_encoded = [ascii_userdata].pack('m0') if ascii_userdata
        end

        def destroy
          requires :id
          service.delete_server(id)
          true
        end

        def images
          requires :id
          service.images(:server => self)
        end

        def all_addresses
          # currently openstack API does not tell us what is a floating ip vs a fixed ip for the vm listing,
          # we fall back to get all addresses and filter sadly.
          # Only includes manually-assigned addresses, not auto-assigned
          @all_addresses ||= service.list_all_addresses.body["floating_ips"].select { |data| data['instance_id'] == id }
        end

        def os_interfaces
          requires :id
          service.os_interfaces(:server => self)
        end

        def reload
          @all_addresses = nil
          super
        end

        # returns all ip_addresses for a given instance
        # this includes both the fixed ip(s) and the floating ip(s)
        def ip_addresses
          addresses ? addresses.values.flatten.collect { |x| x['addr'] } : []
        end

        def floating_ip_addresses
          all_floating = if addresses
                           flattened_values = addresses.values.flatten
                           flattened_values.select { |d| d["OS-EXT-IPS:type"] == "floating" }.collect { |a| a["addr"] }
                         else
                           []
                         end

          # Return them all, leading with manually assigned addresses
          manual = all_addresses.collect { |addr| addr["ip"] }

          all_floating.sort do |a, b|
            a_manual = manual.include? a
            b_manual = manual.include? b

            if a_manual && !b_manual
              -1
            elsif !a_manual && b_manual
              1
            else
              0
            end
          end
          all_floating.empty? ? manual : all_floating
        end

        def public_ip_addresses
          if floating_ip_addresses.empty?
            if addresses
              addresses.select { |s| s[0] =~ /public/i }.collect { |a| a[1][0]['addr'] }
            else
              []
            end
          else
            floating_ip_addresses
          end
        end

        def floating_ip_address
          floating_ip_addresses.first
        end

        def public_ip_address
          public_ip_addresses.first
        end

        def private_ip_addresses
          rfc1918_regexp = /(^10\.|^172\.1[6-9]\.|^172\.2[0-9]\.|^172.3[0-1]\.|^192\.168\.)/
          almost_private = ip_addresses - public_ip_addresses - floating_ip_addresses
          almost_private.select { |ip| rfc1918_regexp.match ip }
        end

        def private_ip_address
          private_ip_addresses.first
        end

        attr_reader :image_ref

        attr_writer :image_ref

        attr_reader :flavor_ref

        attr_writer :flavor_ref

        def ready?
          state == 'ACTIVE'
        end

        def failed?
          state == 'ERROR'
        end

        def change_password(admin_password)
          requires :id
          service.change_server_password(id, admin_password)
          true
        end

        def rebuild(image_ref, name, admin_pass = nil, metadata = nil, personality = nil)
          requires :id
          service.rebuild_server(id, image_ref, name, admin_pass, metadata, personality)
          true
        end

        def resize(flavor_ref)
          requires :id
          service.resize_server(id, flavor_ref)
          true
        end

        def revert_resize
          requires :id
          service.revert_resize_server(id)
          true
        end

        def confirm_resize
          requires :id
          service.confirm_resize_server(id)
          true
        end

        def security_groups
          if id
            requires :id

            groups = service.list_security_groups(:server_id => id).body['security_groups']

            groups.map do |group|
              Fog::OpenStack::Compute::SecurityGroup.new group.merge(:service => service)
            end
          else
            service.security_groups.all
          end
        end

        attr_writer :security_groups

        def reboot(type = 'SOFT')
          requires :id
          service.reboot_server(id, type)
          true
        end

        def stop
          requires :id
          service.stop_server(id)
        end

        def pause
          requires :id
          service.pause_server(id)
        end

        def suspend
          requires :id
          service.suspend_server(id)
        end

        def start
          requires :id

          case state.downcase
          when 'paused'
            service.unpause_server(id)
          when 'suspended'
            service.resume_server(id)
          else
            service.start_server(id)
          end
        end

        def shelve
          requires :id
          service.shelve_server(id)
        end

        def unshelve
          requires :id
          service.unshelve_server(id)
        end

        def shelve_offload
          requires :id
          service.shelve_offload_server(id)
        end

        def create_image(name, metadata = {})
          requires :id
          service.create_image(id, name, metadata)
        end

        def console(log_length = nil)
          requires :id
          service.get_console_output(id, log_length)
        end

        def migrate
          requires :id
          service.migrate_server(id)
        end

        def live_migrate(host, block_migration, disk_over_commit)
          requires :id
          service.live_migrate_server(id, host, block_migration, disk_over_commit)
        end

        def evacuate(host = nil, on_shared_storage = nil, admin_password = nil)
          requires :id
          service.evacuate_server(id, host, on_shared_storage, admin_password)
        end

        def associate_address(floating_ip)
          requires :id
          service.associate_address id, floating_ip
        end

        def disassociate_address(floating_ip)
          requires :id
          service.disassociate_address id, floating_ip
        end

        def reset_vm_state(vm_state)
          requires :id
          service.reset_server_state id, vm_state
        end

        attr_writer :min_count

        attr_writer :max_count

        def networks
          service.networks(:server => self)
        end

        def volumes
          requires :id
          service.volumes.select do |vol|
            vol.attachments.find { |attachment| attachment["serverId"] == id }
          end
        end

        def volume_attachments
          requires :id
          service.get_server_volumes(id).body['volumeAttachments']
        end

        def attach_volume(volume_id, device_name)
          requires :id
          service.attach_volume(volume_id, id, device_name)
          true
        end

        def detach_volume(volume_id)
          requires :id
          service.detach_volume(id, volume_id)
          true
        end

        def save
          raise Fog::Errors::Error, 'Resaving an existing object may create a duplicate' if persisted?
          requires :flavor_ref, :name
          requires_one :image_ref, :block_device_mapping, :block_device_mapping_v2
          options = {
            'personality'             => personality,
            'accessIPv4'              => accessIPv4,
            'accessIPv6'              => accessIPv6,
            'availability_zone'       => availability_zone,
            'user_data'               => user_data_encoded,
            'key_name'                => key_name,
            'config_drive'            => config_drive,
            'security_groups'         => @security_groups,
            'min_count'               => @min_count,
            'max_count'               => @max_count,
            'nics'                    => @nics,
            'os:scheduler_hints'      => @os_scheduler_hints,
            'block_device_mapping'    => @block_device_mapping,
            'block_device_mapping_v2' => @block_device_mapping_v2,
          }
          options['metadata'] = metadata.to_hash unless @metadata.nil?
          options = options.reject { |_key, value| value.nil? }
          data = service.create_server(name, image_ref, flavor_ref, options)
          merge_attributes(data.body['server'])
          true
        end

        def setup(credentials = {})
          requires :ssh_ip_address, :identity, :public_key, :username
          ssh = Fog::SSH.new(ssh_ip_address, username, credentials)
          ssh.run([
                    %(mkdir .ssh),
                    %(echo "#{public_key}" >> ~/.ssh/authorized_keys),
                    %(passwd -l #{username}),
                    %(echo "#{Fog::JSON.encode(attributes)}" >> ~/attributes.json),
                    %(echo "#{Fog::JSON.encode(metadata)}" >> ~/metadata.json)
                  ])
        rescue Errno::ECONNREFUSED
          sleep(1)
          retry
        end
      end
    end
  end
end