Class: Foreman::Model::GCE

Inherits:
ComputeResource show all
Defined in:
app/models/compute_resources/foreman/model/gce.rb

Constant Summary

Constants inherited from ComputeResource

ComputeResource::ALLOWED_KEYBOARD_LAYOUTS

Constants included from EncryptValue

EncryptValue::ENCRYPTION_PREFIX

Constants included from Taxonomix

Taxonomix::TAXONOMY_JOIN_TABLE

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ComputeResource

all_providers, #available_clusters, #available_folders, #available_resource_pools, #available_security_groups, #available_storage_domains, #available_storage_pods, #available_virtual_machines, #available_vnic_profiles, #capable?, #compute_profile_attributes_for, #compute_profile_for, #destroy_vm, #display_type, #display_type=, #editable_network_interfaces?, #find_vm_by_uuid, #host_compute_attrs, #host_interfaces_attrs, #image_exists?, #image_param_name, #keyboard_layout, #keyboard_layout=, #keyboard_layouts, #new_interface, new_provider, #new_volume_errors, #ping, #provider, #provider=, provider_class, #provider_friendly_name, providers, providers_requiring_url, registered_providers, #save_vm, #set_console_password=, #set_console_password?, #start_vm, #stop_vm, #storage_domain, #storage_pod, supported_providers, #supports_host_association?, #supports_update?, #supports_vms_pagination?, #template, #templates, #update_required?, #vm_compute_attributes, #vm_compute_attributes_for, #vm_ready, #vms

Methods included from Authorizable

#authorized?, #check_permissions_after_save

Methods included from PermissionName

#permission_name

Methods included from Encryptable

#encrypt_setters

Methods included from EncryptValue

#decrypt_field, #encrypt_field, #encryption_key, #is_decryptable?, #is_encryptable?, #matches_prefix?, reset_warnings

Methods included from Taxonomix

#add_current_location?, #add_current_organization?, #add_current_taxonomy?, #children_of_selected_location_ids, #children_of_selected_organization_ids, #ensure_taxonomies_not_escalated, #set_current_taxonomy, #used_location_ids, #used_or_selected_location_ids, #used_or_selected_organization_ids, #used_organization_ids

Methods included from DirtyAssociations

#reset_dirty_cache_state

Methods inherited from ApplicationRecord

graphql_type, #logger, logger

Methods included from AuditAssociations::AssociationsDefinitions

#audit_associations, #audited, #configure_dirty_associations, #normalize_associations

Class Method Details

.available?Boolean

Returns:

  • (Boolean)

8
9
10
# File 'app/models/compute_resources/foreman/model/gce.rb', line 8

def self.available?
  Fog::Compute.providers.include?(:google)
end

.image_families_to_filterObject


173
174
175
# File 'app/models/compute_resources/foreman/model/gce.rb', line 173

def self.image_families_to_filter
  @image_families_to_filter ||= []
end

.model_nameObject


185
186
187
# File 'app/models/compute_resources/foreman/model/gce.rb', line 185

def self.model_name
  ComputeResource.model_name
end

.provider_friendly_nameObject


203
204
205
# File 'app/models/compute_resources/foreman/model/gce.rb', line 203

def self.provider_friendly_name
  "Google"
end

.register_family_for_image_filter(family) ⇒ Object

Becomes easy to filter the images list from Google Compute Engine Register an image family using this method to filter list Example - 'rhel', 'centos'


180
181
182
183
# File 'app/models/compute_resources/foreman/model/gce.rb', line 180

def self.register_family_for_image_filter(family)
  image_families_to_filter << family
  image_families_to_filter.uniq!
end

Instance Method Details

#associated_host(vm) ⇒ Object


247
248
249
# File 'app/models/compute_resources/foreman/model/gce.rb', line 247

def associated_host(vm)
  associate_by("ip", [vm.public_ip_address, vm.private_ip_address])
end

#available_imagesObject


160
161
162
163
164
165
166
167
# File 'app/models/compute_resources/foreman/model/gce.rb', line 160

def available_images
  images_list = client.images.current
  if image_families_to_filter.any?
    regexp = Setting.convert_array_to_regexp(image_families_to_filter, prefix: '', suffix: '')
    images_list.select! { |img| img.family&.match(regexp) }
  end
  images_list
end

#available_networks(cluster_id = nil) ⇒ Object


57
58
59
# File 'app/models/compute_resources/foreman/model/gce.rb', line 57

def available_networks(cluster_id = nil)
  client.list_networks.items
end

#capabilitiesObject


16
17
18
# File 'app/models/compute_resources/foreman/model/gce.rb', line 16

def capabilities
  [:image, :new_volume]
end

#connection_optionsObject


251
252
253
# File 'app/models/compute_resources/foreman/model/gce.rb', line 251

def connection_options
  http_proxy ? {:proxy_url => http_proxy.full_url} : {}
end

#console(uuid) ⇒ Object


236
237
238
239
240
241
242
243
244
245
# File 'app/models/compute_resources/foreman/model/gce.rb', line 236

def console(uuid)
  vm = find_vm_by_uuid(uuid)
  if vm.ready?
    {
      'output' =>  vm.serial_port_output, 'timestamp' => Time.now.utc
    }.merge(:type => 'log', :name => vm.name)
  else
    raise ::Foreman::Exception.new(N_("console is not available at this time because the instance is powered off"))
  end
end

#create_vm(args = {}) ⇒ Object


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'app/models/compute_resources/foreman/model/gce.rb', line 124

def create_vm(args = {})
  new_vm(args)
  create_volumes(args)

  username = images.find_by(:uuid => args[:image_id]).try(:username)
  ssh = { :username => username, :public_key => "#{key_pair.public} #{username}" }
  args.merge!(ssh)

  vm = client.servers.create vm_options(args)
  vm.disks.each { |disk| vm.set_disk_auto_delete(true, disk[:device_name]) }
  vm
rescue Fog::Errors::Error => e
  args[:disks].find_all(&:status).map(&:destroy) if args[:disks].present?
  Foreman::Logging.exception("Unhandled GCE error", e)
  raise e
end

#create_volumes(args) ⇒ Object


155
156
157
158
# File 'app/models/compute_resources/foreman/model/gce.rb', line 155

def create_volumes(args)
  args[:disks].map(&:save)
  args[:disks].each { |disk| disk.wait_for { disk.ready? } }
end

#disksObject


66
67
68
# File 'app/models/compute_resources/foreman/model/gce.rb', line 66

def disks
  client.list_disks(zone).items.map(&:name)
end

#emailObject


36
37
38
# File 'app/models/compute_resources/foreman/model/gce.rb', line 36

def email
  attrs[:email]
end

#email=(email) ⇒ Object


40
41
42
# File 'app/models/compute_resources/foreman/model/gce.rb', line 40

def email=(email)
  attrs[:email] = email
end

#image_families_to_filterObject


169
170
171
# File 'app/models/compute_resources/foreman/model/gce.rb', line 169

def image_families_to_filter
  self.class.image_families_to_filter
end

#interfaces_attrs_nameObject


207
208
209
# File 'app/models/compute_resources/foreman/model/gce.rb', line 207

def interfaces_attrs_name
  :network_interfaces
end

#key_pathObject


28
29
30
# File 'app/models/compute_resources/foreman/model/gce.rb', line 28

def key_path
  attrs[:key_path]
end

#key_path=(name) ⇒ Object


32
33
34
# File 'app/models/compute_resources/foreman/model/gce.rb', line 32

def key_path=(name)
  attrs[:key_path] = name
end

#machine_typesObject Also known as: available_flavors


61
62
63
# File 'app/models/compute_resources/foreman/model/gce.rb', line 61

def machine_types
  client.list_machine_types(zone).items
end

#networksObject


53
54
55
# File 'app/models/compute_resources/foreman/model/gce.rb', line 53

def networks
  client.list_networks.items.map(&:name)
end

#new_vm(args = {}) ⇒ Object


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
# File 'app/models/compute_resources/foreman/model/gce.rb', line 78

def new_vm(args = {})
  # convert rails nested_attributes into a plain hash
  [:volumes].each do |collection|
    nested_attrs = args.delete("#{collection}_attributes".to_sym)
    args[collection] = nested_attributes_for(collection, nested_attrs.deep_symbolize_keys) if nested_attrs
  end

  if args[:name].present?
    # With google-api-client >= 0.27.3, this will trigger custom hostname in GCP
    args[:hostname] = args[:name]
    # Dots are not allowed in names
    args[:name] = args[:name].parameterize
  end

  # GCE network interfaces cannot be defined though Foreman yet
  if args[:network]
    args[:network_interfaces] = [{ :network => construct_network(args[:network]) }]
    args.except!(:network)
  end

  if to_bool(args[:associate_external_ip])
    args[:network_interfaces] = construct_network_interfaces(args[:network_interfaces])
  end

  # Note - GCE only supports cloud-init for Container Optimized images and
  # for custom images with cloud-init setup
  if (user_data = args.delete(:user_data)).present?
    args[:metadata] = { :items => [{ :key => 'user-data', :value => user_data }]}
  end

  if args[:volumes].present?
    if args[:image_id].to_i > 0
      image = client.images.find { |i| i.id == args[:image_id].to_i }
      if image.nil?
        raise ::Foreman::Exception.new(N_("selected image does not exist"))
      end
      args[:volumes].first[:source_image] = image.name
    end
    args[:disks] = []
    args[:volumes].each_with_index do |vol_args, i|
      args[:disks] << new_volume(vol_args.merge(:name => "#{args[:name]}-disk#{i + 1}"))
    end
  end
  super(args)
end

#new_volume(attrs = { }) ⇒ Object


211
212
213
214
215
216
217
# File 'app/models/compute_resources/foreman/model/gce.rb', line 211

def new_volume(attrs = { })
  args = {
    :size_gb   => (attrs[:size_gb] || 10).to_i,
    :zone_name => zone,
  }.merge(attrs)
  client.disks.new(args)
end

#normalize_vm_attrs(vm_attrs) ⇒ Object


219
220
221
222
223
224
225
226
227
228
229
230
# File 'app/models/compute_resources/foreman/model/gce.rb', line 219

def normalize_vm_attrs(vm_attrs)
  normalized = slice_vm_attributes(vm_attrs, ['image_id', 'machine_type', 'network'])
  normalized['associate_external_ip'] = to_bool(vm_attrs['associate_external_ip'])
  normalized['image_name'] = images.find_by(:uuid => vm_attrs['image_id']).try(:name)

  volume_attrs = vm_attrs['volumes_attributes'] || {}
  normalized['volumes_attributes'] = volume_attrs.each_with_object({}) do |(key, vol), volumes|
    volumes[key] = { 'size' => memory_gb_to_bytes(vol['size_gb']).to_s }
  end

  normalized
end

#projectObject


20
21
22
# File 'app/models/compute_resources/foreman/model/gce.rb', line 20

def project
  attrs[:project]
end

#project=(name) ⇒ Object


24
25
26
# File 'app/models/compute_resources/foreman/model/gce.rb', line 24

def project=(name)
  attrs[:project] = name
end

#provided_attributesObject


44
45
46
# File 'app/models/compute_resources/foreman/model/gce.rb', line 44

def provided_attributes
  super.merge({ :ip => :vm_ip_address })
end

#setup_key_pairObject


189
190
191
192
193
194
# File 'app/models/compute_resources/foreman/model/gce.rb', line 189

def setup_key_pair
  require 'sshkey'
  name = "foreman-#{id}#{Foreman.uuid}"
  key  = ::SSHKey.generate
  build_key_pair :name => name, :secret => key.private_key, :public => key.ssh_public_key
end

#test_connection(options = {}) ⇒ Object


196
197
198
199
200
201
# File 'app/models/compute_resources/foreman/model/gce.rb', line 196

def test_connection(options = {})
  super
  errors[:user].empty? && errors[:password].empty? && zones
rescue => e
  errors[:base] << e.message
end

#to_labelObject


12
13
14
# File 'app/models/compute_resources/foreman/model/gce.rb', line 12

def to_label
  "#{name} (#{zone}-#{provider_friendly_name})"
end

#user_data_supported?Boolean

Returns:

  • (Boolean)

232
233
234
# File 'app/models/compute_resources/foreman/model/gce.rb', line 232

def user_data_supported?
  true
end

#vm_options(args) ⇒ Object


141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'app/models/compute_resources/foreman/model/gce.rb', line 141

def vm_options(args)
  options = vm_instance_defaults.merge(args.to_hash.deep_symbolize_keys)

  # deep_symbolize_keys required for :network_interfaces,:metadata
  # fog-google processes these keys & creates objects of respective
  # classes from ::Google::Apis::ComputeV1
  server_optns = options.slice!(:network_interfaces, :metadata)

  # HashWithIndifferentAccess won't work here.
  # server_optns.symbolize_keys required as ::Google::Apis::ComputeV1 classes
  # only accepts keyword arguments while initializing instance.
  options.to_hash.deep_symbolize_keys.merge(server_optns.symbolize_keys)
end

#zoneObject


70
71
72
# File 'app/models/compute_resources/foreman/model/gce.rb', line 70

def zone
  url
end

#zone=(zone) ⇒ Object


74
75
76
# File 'app/models/compute_resources/foreman/model/gce.rb', line 74

def zone=(zone)
  self.url = zone
end

#zonesObject Also known as: available_zones


48
49
50
# File 'app/models/compute_resources/foreman/model/gce.rb', line 48

def zones
  client.list_zones.items.map(&:name)
end