foreman_google 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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