foreman_google 0.0.1 → 1.0.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -4
  3. data/app/controllers/concerns/foreman/controller/parameters/compute_resource_extension.rb +21 -0
  4. data/app/controllers/foreman_google/api/v2/apipie_extensions.rb +16 -0
  5. data/app/controllers/foreman_google/api/v2/compute_resources_extensions.rb +22 -0
  6. data/app/lib/foreman_google/google_compute_adapter.rb +178 -0
  7. data/app/lib/google_cloud_compute/compute_attributes.rb +98 -0
  8. data/app/lib/google_cloud_compute/compute_collection.rb +23 -0
  9. data/app/lib/google_extensions/attached_disk.rb +22 -0
  10. data/app/models/concerns/foreman_google/host_managed_extensions.rb +11 -0
  11. data/app/models/foreman_google/gce.rb +102 -38
  12. data/app/models/foreman_google/google_compute.rb +68 -58
  13. data/app/views/compute_resources/form/_gce.html.erb +10 -0
  14. data/app/views/compute_resources/show/_gce.html.erb +0 -0
  15. data/app/views/compute_resources_vms/form/gce/_base.html.erb +18 -0
  16. data/app/views/compute_resources_vms/form/gce/_volume.html.erb +5 -0
  17. data/app/views/compute_resources_vms/index/_gce.html.erb +26 -0
  18. data/app/views/compute_resources_vms/show/_gce.html.erb +21 -0
  19. data/app/views/images/form/_gce.html.erb +3 -0
  20. data/db/migrate/20220331113745_foreman_gce_to_foreman_google_gce.rb +24 -0
  21. data/lib/foreman_google/engine.rb +10 -2
  22. data/lib/foreman_google/version.rb +1 -1
  23. data/locale/action_names.rb +6 -0
  24. data/locale/en/foreman_google.edit.po +116 -0
  25. data/locale/en/foreman_google.po +74 -2
  26. data/locale/en/foreman_google.po.time_stamp +0 -0
  27. data/locale/foreman_google.pot +112 -8
  28. data/locale/gemspec.rb +1 -1
  29. data/package.json +7 -7
  30. data/test/fixtures/disks_delete.json +14 -0
  31. data/test/fixtures/disks_get.json +13 -0
  32. data/test/fixtures/disks_insert.json +12 -0
  33. data/test/fixtures/instance.json +1 -1
  34. data/test/fixtures/instance_insert.json +15 -0
  35. data/test/fixtures/instance_list.json +86 -0
  36. data/test/fixtures/instance_set_disk_auto_delete.json +14 -0
  37. data/test/fixtures/operation_error.json +26 -0
  38. data/test/fixtures/operation_get.json +13 -0
  39. data/test/models/foreman_google/gce_test.rb +43 -5
  40. data/test/models/foreman_google/google_compute_test.rb +90 -32
  41. data/test/unit/foreman_google/google_compute_adapter_test.rb +103 -4
  42. data/test/unit/google_extensions/attached_disk_test.rb +17 -0
  43. data/webpack/global_index.js +2 -13
  44. data/webpack/legacy.js +16 -0
  45. metadata +66 -16
  46. data/app/controllers/concerns/foreman_google/temporary_prepend_path.rb +0 -16
  47. data/lib/foreman_google/google_compute_adapter.rb +0 -91
@@ -1,25 +1,19 @@
1
+ require 'google/apis/compute_v1'
2
+
1
3
  module ForemanGoogle
2
4
  class GoogleCompute
3
- attr_reader :identity, :name, :hostname, :machine_type, :network_interfaces,
4
- :associate_external_ip, :image_id, :disks, :metadata
5
+ attr_reader :identity, :name, :hostname, :creation_timestamp, :machine_type, :network_interfaces, :volumes,
6
+ :associate_external_ip, :network, :zone, :zone_name, :image_id, :disks, :metadata
5
7
 
6
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
7
- def initialize(client:, zone:, identity: nil, args: {})
8
+ def initialize(client:, zone:, identity: nil, instance: nil, args: {})
8
9
  @client = client
9
10
  @zone = zone
10
11
  @identity = identity
12
+ @instance = instance
11
13
 
12
- @name = parameterize_name(args[:name])
13
- @hostname = @name
14
- @machine_type = args[:machine_type]
15
- @network_interfaces = construct_network(args[:network] || 'default', args[:associate_external_ip] || '0', args[:network_interfaces] || [])
16
- @image_id = args[:image_id]
17
- @disks = load_disks(args[:image_id], args[:volumes])
18
- @metadata = construct_metadata(args[:user_data])
19
-
20
- identity && load
14
+ load if identity && instance.nil?
15
+ load_attributes(args)
21
16
  end
22
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
23
17
 
24
18
  def persisted?
25
19
  !!identity
@@ -38,8 +32,9 @@ module ForemanGoogle
38
32
  # @returns [String] one of PROVISIONING, STAGING, RUNNING, STOPPING, SUSPENDING, SUSPENDED, REPAIRING, and TERMINATED
39
33
  # if nil, instance is not persisted as VM on GCE
40
34
  def status
41
- persisted? && instance.status
35
+ persisted? && @instance.status
42
36
  end
37
+ alias_method :state, :status
43
38
 
44
39
  def start
45
40
  raise Foreman::Exception('unable to start machine that is not persisted') unless persisted?
@@ -52,76 +47,91 @@ module ForemanGoogle
52
47
  end
53
48
 
54
49
  def to_s
50
+ @name
55
51
  end
56
52
 
57
53
  def interfaces
58
54
  @network_interfaces
59
55
  end
60
56
 
61
- def interfaces_attributes=(attrs)
57
+ def create_volumes
58
+ @volumes.each do |vol|
59
+ @client.insert_disk(@zone, vol.insert_attrs)
60
+ wait_for { @client.disk(@zone, vol.device_name).status == 'READY' }
61
+ end
62
62
  end
63
63
 
64
- def volumes
65
- @disks
64
+ def destroy_volumes
65
+ @volumes.each do |volume|
66
+ @client.delete_disk(@zone, volume.device_name)
67
+ end
66
68
  end
67
69
 
68
- private
70
+ def create_instance
71
+ args = GoogleCloudCompute::ComputeAttributes.new(@client).for_create(self)
72
+ @client.insert_instance(@zone, args)
73
+ end
69
74
 
70
- def instance
71
- return unless identity
72
- @instance || load
75
+ def set_disk_auto_delete
76
+ @client.set_disk_auto_delete(@zone, @name)
73
77
  end
74
78
 
75
- def load
76
- @instance = @client.instance(@zone.split('/').last, identity)
79
+ def pretty_machine_type
80
+ return @machine_type unless @instance
81
+ @instance.machine_type.split('/').last
77
82
  end
78
83
 
79
- def parameterize_name(name)
80
- name&.parameterize || "foreman_#{Time.now.to_i}"
84
+ def vm_description
85
+ pretty_machine_type
81
86
  end
82
87
 
83
- def construct_network(network_name, associate_external_ip, network_interfaces)
84
- # handle network_interface for external ip
85
- # assign ephemeral external IP address using associate_external_ip
86
- if ActiveModel::Type::Boolean.new.cast(associate_external_ip)
87
- network_interfaces = [{ network: 'global/networks/default' }] if network_interfaces.empty?
88
- access_config = { name: 'External NAT', type: 'ONE_TO_ONE_NAT' }
88
+ def vm_ip_address
89
+ return if @instance.network_interfaces.empty?
89
90
 
90
- # Note - no support for external_ip from foreman
91
- # access_config[:nat_ip] = external_ip if external_ip
92
- network_interfaces[0][:access_configs] = [access_config]
93
- return network_interfaces
94
- end
91
+ @instance.network_interfaces.first.access_configs.first&.nat_i_p
92
+ end
93
+ alias_method :public_ip_address, :vm_ip_address
94
+
95
+ def private_ip_address
96
+ return unless @instance.network_interfaces.any?
95
97
 
96
- network = "https://compute.googleapis.com/compute/v1/projects/#{@client.project_id}/global/networks/#{network_name}"
97
- [{ network: network }]
98
+ @instance.network_interfaces.first.network_i_p
98
99
  end
99
100
 
100
- def load_image(image_id)
101
- return unless image_id
101
+ def pretty_image_name
102
+ return unless @instance.disks.any?
103
+
104
+ disk_name = @instance.disks.first.source.split('/').last
105
+ image_name = @client.disk(@zone_name, disk_name).source_image
102
106
 
103
- image = @client.images.find { |img| img.id == image_id.to_i }
104
- raise ::Foreman::Exception, N_('selected image does not exist') if image.nil?
105
- image
107
+ image_name.split('/').last
106
108
  end
107
109
 
108
- def load_disks(image_id, volumes = [])
109
- return [] if volumes.empty?
110
- image = load_image(image_id)
110
+ def volumes_attributes=(_attrs)
111
+ end
111
112
 
112
- volumes.first[:source_image] = image.name if image
113
- # TODO: Is OpenStruct enough to replace Fog::Compute::Google::Disk
114
- # or do we need our own class?
115
- volumes.map.with_index do |vol_attrs, i|
116
- OpenStruct.new(**vol_attrs.merge(name: "#{@name}-disk#{i + 1}"))
117
- end
113
+ def serial_port_output
114
+ @client.serial_port_output(@zone, @identity)&.contents
115
+ end
116
+
117
+ def ip_addresses
118
+ [vm_ip_address, private_ip_address]
119
+ end
120
+
121
+ def wait_for(&block)
122
+ @client.wait_for(&block)
123
+ end
124
+
125
+ private
126
+
127
+ def load
128
+ @instance = @client.instance(@zone.split('/').last, identity)
118
129
  end
119
130
 
120
- # Note - GCE only supports cloud-init for Container Optimized images and
121
- # for custom images with cloud-init setup
122
- def construct_metadata(user_data)
123
- return if user_data.blank?
124
- { items: [{ key: 'user-data', value: user_data }] }
131
+ def load_attributes(args_for_new)
132
+ klass = GoogleCloudCompute::ComputeAttributes.new(@client)
133
+ attrs = @instance ? klass.for_instance(@instance) : klass.for_new(args_for_new)
134
+ attrs.each { |name, value| instance_variable_set("@#{name}", value) }
125
135
  end
126
136
  end
127
137
  end
@@ -0,0 +1,10 @@
1
+ <%= http_proxy_field f %>
2
+
3
+ <%= textarea_f f, :password, :label => _("JSON key"),
4
+ :help_inline => file_field(:password, :json, accept: "application/JSON", onchange: 'tfm.gce.jsonLoader(this)'),
5
+ :id => 'gce_json', :class => 'col-md-8' %>
6
+
7
+ <% zones = f.object.zones rescue [] %>
8
+
9
+ <%= selectable_f(f, :zone, zones, {}, {:label => _('Zone'), :disabled => zones.empty?,
10
+ :help_inline_permanent => load_button_f(f, zones.any?, _("Load Zones")) }) %>
File without changes
@@ -0,0 +1,18 @@
1
+ <%= select_f f, :machine_type, compute_resource.machine_types, :name, :name,
2
+ { :selected => f.object.pretty_machine_type },
3
+ { :label => _('Machine type'), :label_size => "col-md-2" } %>
4
+
5
+ <% if controller_name != "compute_attributes" %>
6
+ <%
7
+ arch ||= nil ; os ||= nil
8
+ images = possible_images(compute_resource, arch, os)
9
+ %>
10
+ <div id='image_selection'>
11
+ <%= select_f f, :image_id, images, :uuid, :name,
12
+ { :include_blank => (images.empty? || images.size == 1) ? false : _('Please select an image') },
13
+ { :disabled => images.empty?, :label => _('Image'), :label_size => "col-md-2" } %>
14
+ </div>
15
+ <% end %>
16
+
17
+ <%= selectable_f f, :network, compute_resource.networks, {}, :label => _('Network'), :label_size => "col-md-2" %>
18
+ <%= checkbox_f f, :associate_external_ip, :label => _('Associate Ephemeral External IP'), :label_size => "col-md-2" %>
@@ -0,0 +1,5 @@
1
+ <%
2
+ # For backwards compatibility, Fog has :size_gb, Google has :disk_size_gb
3
+ field_name = f.object.class == Google::Cloud::Compute::V1::AttachedDisk ? :disk_size_gb : :size_gb
4
+ %>
5
+ <%= text_f f, field_name, class: "col-md-2", label: _("Size (GB)"), label_size: "col-md-2", onchange: 'tfm.computeResource.capacityEdit(this)' %>
@@ -0,0 +1,26 @@
1
+ <thead>
2
+ <tr>
3
+ <th><%= _('Name') %></th>
4
+ <th><%= _('Type') %></th>
5
+ <th><%= _('State') %></th>
6
+ <th><%= _('Actions') %></th>
7
+ </tr>
8
+ </thead>
9
+ <tbody>
10
+ <% @vms.each do |vm| %>
11
+ <%
12
+ view_path = hash_for_compute_resource_vm_path(compute_resource_id: @compute_resource, id: vm.identity).merge(auth_object: @compute_resource.id, auth_action: 'view', authorizer: authorizer)
13
+ delete_link = display_delete_if_authorized(hash_for_compute_resource_vm_path(compute_resource_id: @compute_resource, id: vm.identity).merge(auth_object: @compute_resource, authorizer: authorizer))
14
+ %>
15
+ <tr>
16
+ <td><%= link_to_if_authorized vm.name, view_path %></td>
17
+ <td><%= vm.pretty_machine_type %></td>
18
+ <td><%= vm.status.downcase %></td>
19
+ <td>
20
+ <%= action_buttons(vm_power_action(vm, authorizer),
21
+ vm_import_action(vm),vm_associate_link(vm),
22
+ delete_link) %>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
@@ -0,0 +1,21 @@
1
+ <% title @vm.name %>
2
+
3
+ <div class='col-md-12'>
4
+ <table class="<%= table_css_classes %>">
5
+ <thead>
6
+ <tr><th colspan="2"><%=_("Properties") %></th></tr>
7
+ </thead>
8
+ <tbody>
9
+ <%= prop :state %>
10
+ <%= prop :pretty_machine_type, _("Machine Type") %>
11
+ <%= prop :zone_name, _("Zone") %>
12
+ <tr>
13
+ <td>Created</td>
14
+ <td><%= date_time_relative(@vm.creation_timestamp) %></td>
15
+ </tr>
16
+ <%= prop :private_ip_address %>
17
+ <%= prop :public_ip_address %>
18
+ <%= prop :pretty_image_name, _("Image") %>
19
+ </tbody>
20
+ </table>
21
+ </div>
@@ -0,0 +1,3 @@
1
+ <%= text_f f, :username, :value => @image.username || "user", :help_inline => _("The user that is used to ssh into the instance, normally cloud-user, ec2-user, ubuntu, etc. Note: Google engine doesn't support SSH for the root user.") %>
2
+ <%= image_field(f) %>
3
+ <%= checkbox_f f, :user_data, :help_inline => _("Does this image support user data input (e.g. via cloud-init)?") %>
@@ -0,0 +1,24 @@
1
+ # rubocop:disable Metrics/MethodLength
2
+ class ForemanGceToForemanGoogleGce < ActiveRecord::Migration[6.0]
3
+ def up
4
+ User.without_auditing do
5
+ original_type = 'Foreman::Model::GCE'
6
+ new_type = 'ForemanGoogle::GCE'
7
+
8
+ # First update the type to avoid error:
9
+ # ActiveRecord::SubclassNotFound: The single-table inheritance
10
+ # mechanism failed to locate the subclass: 'Foreman::Model::GCE'
11
+ ComputeResource.unscoped.where(type: original_type).update_all(type: new_type)
12
+
13
+ ComputeResource.unscoped.where(type: new_type).each do |cr|
14
+ unless cr.attrs[:key_path]
15
+ say("Compute resource [#{cr.name}] is missing path to JSON key file, can't load the data. Please update the resource manually.")
16
+ next
17
+ end
18
+ json_data = File.read(cr.attrs[:key_path])
19
+ cr.update(password: json_data)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ # rubocop:enable Metrics/MethodLength
@@ -12,7 +12,8 @@ module ForemanGoogle
12
12
 
13
13
  initializer 'foreman_google.register_plugin', before: :finisher_hook do |_app|
14
14
  Foreman::Plugin.register :foreman_google do
15
- requires_foreman '>= 2.4.0'
15
+ requires_foreman '>= 3.5.0'
16
+ register_global_js_file 'global'
16
17
 
17
18
  in_to_prepare do
18
19
  compute_resource(ForemanGoogle::GCE)
@@ -22,7 +23,14 @@ module ForemanGoogle
22
23
 
23
24
  # Include concerns in this config.to_prepare block
24
25
  config.to_prepare do
25
- ::ComputeResourcesController.include ForemanGoogle::TemporaryPrependPath
26
+ require 'google/cloud/compute/v1'
27
+
28
+ ::Host::Managed.include ForemanGoogle::HostManagedExtensions
29
+ ::Api::V2::ComputeResourcesController.include ForemanGoogle::Api::V2::ComputeResourcesExtensions
30
+ ::Api::V2::ComputeResourcesController.include ForemanGoogle::Api::V2::ApipieExtensions
31
+ ::Api::V2::ComputeResourcesController.include Foreman::Controller::Parameters::ComputeResourceExtension
32
+ ::ComputeResourcesController.include Foreman::Controller::Parameters::ComputeResourceExtension
33
+ Google::Cloud::Compute::V1::AttachedDisk.include GoogleExtensions::AttachedDisk
26
34
  rescue StandardError => e
27
35
  Rails.logger.warn "ForemanGoogle: skipping engine hook (#{e})"
28
36
  end
@@ -1,3 +1,3 @@
1
1
  module ForemanGoogle
2
- VERSION = '0.0.1'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -0,0 +1,6 @@
1
+ # Autogenerated!
2
+ _('Preupgrade job')
3
+ _('Action with sub plans')
4
+ _('Import facts')
5
+ _('Import Puppet classes')
6
+ _('Remote action:')
@@ -0,0 +1,116 @@
1
+ # foreman_google
2
+ #
3
+ # This file is distributed under the same license as foreman_google.
4
+ #
5
+ msgid ""
6
+ msgstr ""
7
+ "Project-Id-Version: version 0.0.1\n"
8
+ "Report-Msgid-Bugs-To: \n"
9
+ "PO-Revision-Date: 2014-08-20 08:54+0100\n"
10
+ "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
11
+ "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
12
+ "Language: \n"
13
+ "MIME-Version: 1.0\n"
14
+ "Content-Type: text/plain; charset=UTF-8\n"
15
+ "Content-Transfer-Encoding: 8bit\n"
16
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
17
+
18
+ #: ../app/models/foreman_google/gce.rb:126
19
+ msgid "console is not available at this time because the instance is powered off"
20
+ msgstr ""
21
+
22
+ #: ../app/models/foreman_google/gce.rb:170
23
+ msgid "Missing an image for operating system!"
24
+ msgstr ""
25
+
26
+ #: ../app/views/compute_resources/form/_gce.html.erb:3
27
+ msgid "JSON key"
28
+ msgstr ""
29
+
30
+ #: ../app/views/compute_resources/form/_gce.html.erb:9 ../app/views/compute_resources_vms/show/_gce.html.erb:11
31
+ msgid "Zone"
32
+ msgstr ""
33
+
34
+ #: ../app/views/compute_resources/form/_gce.html.erb:10
35
+ msgid "Load Zones"
36
+ msgstr ""
37
+
38
+ #: ../app/views/compute_resources_vms/form/gce/_base.html.erb:3
39
+ msgid "Machine type"
40
+ msgstr ""
41
+
42
+ #: ../app/views/compute_resources_vms/form/gce/_base.html.erb:12
43
+ msgid "Please select an image"
44
+ msgstr ""
45
+
46
+ #: ../app/views/compute_resources_vms/form/gce/_base.html.erb:13 ../app/views/compute_resources_vms/show/_gce.html.erb:18
47
+ msgid "Image"
48
+ msgstr ""
49
+
50
+ #: ../app/views/compute_resources_vms/form/gce/_base.html.erb:17
51
+ msgid "Network"
52
+ msgstr ""
53
+
54
+ #: ../app/views/compute_resources_vms/form/gce/_base.html.erb:18
55
+ msgid "Associate Ephemeral External IP"
56
+ msgstr ""
57
+
58
+ #: ../app/views/compute_resources_vms/form/gce/_volume.html.erb:5
59
+ msgid "Size (GB)"
60
+ msgstr ""
61
+
62
+ #: ../app/views/compute_resources_vms/index/_gce.html.erb:3
63
+ msgid "Name"
64
+ msgstr ""
65
+
66
+ #: ../app/views/compute_resources_vms/index/_gce.html.erb:4
67
+ msgid "Type"
68
+ msgstr ""
69
+
70
+ #: ../app/views/compute_resources_vms/index/_gce.html.erb:5
71
+ msgid "State"
72
+ msgstr ""
73
+
74
+ #: ../app/views/compute_resources_vms/index/_gce.html.erb:6
75
+ msgid "Actions"
76
+ msgstr ""
77
+
78
+ #: ../app/views/compute_resources_vms/show/_gce.html.erb:6
79
+ msgid "Properties"
80
+ msgstr ""
81
+
82
+ #: ../app/views/compute_resources_vms/show/_gce.html.erb:10
83
+ msgid "Machine Type"
84
+ msgstr ""
85
+
86
+ #: ../app/views/images/form/_gce.html.erb:1
87
+ msgid "The user that is used to ssh into the instance, normally cloud-user, ec2-user, ubuntu, etc. Note: Google engine doesn't support SSH for the root user."
88
+ msgstr ""
89
+
90
+ #: ../app/views/images/form/_gce.html.erb:3
91
+ msgid "Does this image support user data input (e.g. via cloud-init)?"
92
+ msgstr ""
93
+
94
+ #: action_names.rb:2
95
+ msgid "Preupgrade job"
96
+ msgstr ""
97
+
98
+ #: action_names.rb:3
99
+ msgid "Action with sub plans"
100
+ msgstr ""
101
+
102
+ #: action_names.rb:4
103
+ msgid "Import facts"
104
+ msgstr ""
105
+
106
+ #: action_names.rb:5
107
+ msgid "Import Puppet classes"
108
+ msgstr ""
109
+
110
+ #: action_names.rb:6
111
+ msgid "Remote action:"
112
+ msgstr ""
113
+
114
+ #: gemspec.rb:2
115
+ msgid "Google Compute Engine plugin for the Foreman."
116
+ msgstr ""
@@ -2,12 +2,10 @@
2
2
  #
3
3
  # This file is distributed under the same license as foreman_google.
4
4
  #
5
- #, fuzzy
6
5
  msgid ""
7
6
  msgstr ""
8
7
  "Project-Id-Version: version 0.0.1\n"
9
8
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2014-08-20 08:46+0100\n"
11
9
  "PO-Revision-Date: 2014-08-20 08:54+0100\n"
12
10
  "Last-Translator: Foreman Team <foreman-dev@googlegroups.com>\n"
13
11
  "Language-Team: Foreman Team <foreman-dev@googlegroups.com>\n"
@@ -17,3 +15,77 @@ msgstr ""
17
15
  "Content-Transfer-Encoding: 8bit\n"
18
16
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
17
 
18
+ msgid "Action with sub plans"
19
+ msgstr ""
20
+
21
+ msgid "Actions"
22
+ msgstr ""
23
+
24
+ msgid "Associate Ephemeral External IP"
25
+ msgstr ""
26
+
27
+ msgid "Does this image support user data input (e.g. via cloud-init)?"
28
+ msgstr ""
29
+
30
+ msgid "Google Compute Engine plugin for the Foreman."
31
+ msgstr ""
32
+
33
+ msgid "Image"
34
+ msgstr ""
35
+
36
+ msgid "Import Puppet classes"
37
+ msgstr ""
38
+
39
+ msgid "Import facts"
40
+ msgstr ""
41
+
42
+ msgid "JSON key"
43
+ msgstr ""
44
+
45
+ msgid "Load Zones"
46
+ msgstr ""
47
+
48
+ msgid "Machine Type"
49
+ msgstr ""
50
+
51
+ msgid "Machine type"
52
+ msgstr ""
53
+
54
+ msgid "Missing an image for operating system!"
55
+ msgstr ""
56
+
57
+ msgid "Name"
58
+ msgstr ""
59
+
60
+ msgid "Network"
61
+ msgstr ""
62
+
63
+ msgid "Please select an image"
64
+ msgstr ""
65
+
66
+ msgid "Preupgrade job"
67
+ msgstr ""
68
+
69
+ msgid "Properties"
70
+ msgstr ""
71
+
72
+ msgid "Remote action:"
73
+ msgstr ""
74
+
75
+ msgid "Size (GB)"
76
+ msgstr ""
77
+
78
+ msgid "State"
79
+ msgstr ""
80
+
81
+ msgid "The user that is used to ssh into the instance, normally cloud-user, ec2-user, ubuntu, etc. Note: Google engine doesn't support SSH for the root user."
82
+ msgstr ""
83
+
84
+ msgid "Type"
85
+ msgstr ""
86
+
87
+ msgid "Zone"
88
+ msgstr ""
89
+
90
+ msgid "console is not available at this time because the instance is powered off"
91
+ msgstr ""
File without changes