vm_shepherd 1.0.3 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 219c652eba8784d7e3d73dca6d47434251d457cf
4
- data.tar.gz: d93a9bed83c27bbd86d75f39bcecd8ce11d88549
3
+ metadata.gz: 316f95673f45c873990de6c9a8313e9f7c30c14d
4
+ data.tar.gz: a5cacb70390f6f42dfbac300b298cf720eaaaf96
5
5
  SHA512:
6
- metadata.gz: 50eb9bb8a26e7f8f7d566fb70c63746a04b90c5b341a498139c5d2e7edeb73ebd1dba1f09799dfc498d78b131517ab7f7f88a64d27ec871e0e6b739d7d16f7a6
7
- data.tar.gz: 599c01489ea1bcc65321c8f331402028f5e1e63cd9573fa1b67084031293c1f8ae596013c11629207694649e8319113a60fa86c6a600c924a8a5f454bf1f6a3e
6
+ metadata.gz: 21c6f2e27bf812d8349adabb9c91a2f360b29c94a17a359ede7c099d927af52ce80e78ae39f071f47bd07a64ccded8b52ab060be96cf608daf38da7e5050e607
7
+ data.tar.gz: c43837145203de571a44d3617e5b295036a78b2d956112ea9a5c42c34fd49e35fad534f34f6bddb09a5f7b4a2637992dcd8a655343fde52540c000e06d7894d2
@@ -11,67 +11,120 @@ module VmShepherd
11
11
  RETRY_INTERVAL = 5
12
12
  DO_NOT_TERMINATE_TAG_KEY = 'do_not_terminate'
13
13
 
14
- def initialize(aws_options)
14
+ def initialize(env_config)
15
15
  AWS.config(
16
- access_key_id: aws_options.fetch(:aws_access_key),
17
- secret_access_key: aws_options.fetch(:aws_secret_key),
16
+ access_key_id: env_config.fetch(:aws_access_key),
17
+ secret_access_key: env_config.fetch(:aws_secret_key),
18
18
  region: AWS_REGION
19
19
  )
20
- @aws_options = aws_options
20
+ @env_config = env_config
21
21
  end
22
22
 
23
- def deploy(ami_file_path)
23
+ def prepare_environment(cloudformation_template_file)
24
+ template = File.read(cloudformation_template_file)
25
+
26
+ cfm = AWS::CloudFormation.new
27
+ stack = cfm.stacks.create(env_config.fetch(:stack_name), template, :parameters => env_config.fetch(:parameters), capabilities: ['CAPABILITY_IAM'])
28
+
29
+ retry_until(retry_limit: 360) do
30
+ status = stack.status
31
+ case status
32
+ when 'CREATE_COMPLETE'
33
+ true
34
+ when 'CREATE_IN_PROGRESS'
35
+ false
36
+ when 'ROLLBACK_IN_PROGRESS'
37
+ false
38
+ else
39
+ stack.delete if status == 'ROLLBACK_COMPLETE'
40
+ raise "Unexpected status for stack #{env_config.fetch(:stack_name)} : #{status}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def deploy(ami_file_path:, vm_config:)
24
46
  image_id = File.read(ami_file_path).strip
25
47
 
26
48
  instance =
27
- retry_ignoring_error_until(AWS::EC2::Errors::InvalidIPAddress::InUse) do
28
- AWS.ec2.instances.create(
29
- image_id: image_id,
30
- key_name: aws_options.fetch(:ssh_key_name),
31
- security_group_ids: [aws_options.fetch(:security_group_id)],
32
- subnet: aws_options.fetch(:public_subnet_id),
33
- instance_type: OPS_MANAGER_INSTANCE_TYPE
34
- )
49
+ retry_until do
50
+ begin
51
+ AWS.ec2.instances.create(
52
+ image_id: image_id,
53
+ key_name: env_config.fetch(:outputs).fetch(:ssh_key_name),
54
+ security_group_ids: [env_config.fetch(:outputs).fetch(:security_group)],
55
+ subnet: env_config.fetch(:outputs).fetch(:public_subnet_id),
56
+ instance_type: OPS_MANAGER_INSTANCE_TYPE
57
+ )
58
+ rescue AWS::EC2::Errors::InvalidIPAddress::InUse
59
+ false
60
+ end
35
61
  end
36
62
 
37
- retry_ignoring_error_until(AWS::EC2::Errors::InvalidInstanceID::NotFound) do
38
- instance.status == :running
63
+ retry_until do
64
+ begin
65
+ instance.status == :running
66
+ rescue AWS::EC2::Errors::InvalidInstanceID::NotFound
67
+ false
68
+ end
39
69
  end
40
70
 
41
- instance.associate_elastic_ip(aws_options.fetch(:elastic_ip_id))
42
- instance.add_tag('Name', value: aws_options.fetch(:vm_name))
71
+ elastic_ip = AWS.ec2.elastic_ips.create(vpc: true)
72
+ instance.associate_elastic_ip(elastic_ip.allocation_id)
73
+ instance.add_tag('Name', value: vm_config.fetch(:vm_name))
43
74
  end
44
75
 
45
76
  def clean_environment
46
- subnets = [
47
- AWS.ec2.subnets[aws_options.fetch(:public_subnet_id)],
48
- AWS.ec2.subnets[aws_options.fetch(:private_subnet_id)]
49
- ]
77
+ subnets = []
78
+ subnets << AWS.ec2.subnets[env_config.fetch(:outputs).fetch(:public_subnet_id)] if env_config.fetch(:outputs).fetch(:public_subnet_id)
79
+ subnets << AWS.ec2.subnets[env_config.fetch(:outputs).fetch(:private_subnet_id)] if env_config.fetch(:outputs).fetch(:private_subnet_id)
50
80
 
51
81
  volumes = []
52
82
  subnets.each do |subnet|
53
83
  subnet.instances.each do |instance|
54
- unless instance.tags.to_h.fetch(DO_NOT_TERMINATE_TAG_KEY, false)
55
- instance.attachments.each do |_, attachment|
56
- volumes.push(attachment.volume) unless attachment.delete_on_termination
57
- end
58
- instance.terminate
84
+ instance.attachments.each do |_, attachment|
85
+ volumes.push(attachment.volume) unless attachment.delete_on_termination
59
86
  end
87
+ instance.terminate
60
88
  end
61
89
  end
62
90
  destroy_volumes(volumes)
91
+
92
+ cfm = AWS::CloudFormation.new
93
+ stack = cfm.stacks[env_config.fetch(:stack_name)]
94
+ stack.delete
95
+ retry_until(retry_limit: 360) do
96
+ begin
97
+ status = stack.status
98
+ case status
99
+ when 'DELETE_COMPLETE'
100
+ true
101
+ when 'DELETE_IN_PROGRESS'
102
+ false
103
+ else
104
+ raise "Unexpected status for stack #{env_config.fetch(:stack_name)} : #{status}"
105
+ end
106
+ rescue AWS::CloudFormation::Errors::ValidationError
107
+ raise if stack.exists?
108
+ true
109
+ end
110
+ end if stack
63
111
  end
64
112
 
65
- def destroy
66
- AWS.ec2.elastic_ips.each do |elastic_ip|
67
- if elastic_ip.allocation_id == aws_options.fetch(:elastic_ip_id)
68
- elastic_ip.instance.terminate if elastic_ip.instance
113
+ def destroy(vm_config)
114
+ AWS.ec2.instances.each do |instance|
115
+ if instance.tags.to_h['Name'] == vm_config.fetch(:vm_name)
116
+ elastic_ip = instance.elastic_ip
117
+ if elastic_ip
118
+ elastic_ip.disassociate
119
+ elastic_ip.delete
120
+ end
121
+ instance.terminate
69
122
  end
70
123
  end
71
124
  end
72
125
 
73
126
  private
74
- attr_reader :aws_options
127
+ attr_reader :env_config
75
128
 
76
129
  def destroy_volumes(volumes)
77
130
  volumes.each do |volume|
@@ -84,19 +137,15 @@ module VmShepherd
84
137
  end
85
138
  end
86
139
 
87
- def retry_ignoring_error_until(exception_class, &block)
140
+ def retry_until(retry_limit: RETRY_LIMIT, &block)
88
141
  tries = 0
89
142
  condition_reached = false
90
143
  loop do
91
- begin
92
- tries += 1
93
- raise(RetryLimitExceeded) if tries > RETRY_LIMIT
94
- condition_reached = block.call
95
- sleep RETRY_INTERVAL
96
- rescue exception_class
97
- retry
98
- end
144
+ tries += 1
145
+ raise(RetryLimitExceeded) if tries > retry_limit
146
+ condition_reached = block.call
99
147
  break if condition_reached
148
+ sleep RETRY_INTERVAL
100
149
  end
101
150
  condition_reached
102
151
  end
@@ -99,6 +99,9 @@ module VmShepherd
99
99
  end
100
100
  end
101
101
 
102
+ def prepare_environment
103
+ end
104
+
102
105
  def service
103
106
  @service ||= Fog::Compute.new(
104
107
  provider: 'openstack',
@@ -1,6 +1,7 @@
1
1
  module VmShepherd
2
2
  class Shepherd
3
- class InvalidIaas < StandardError; end
3
+ class InvalidIaas < StandardError;
4
+ end
4
5
 
5
6
  def initialize(settings:)
6
7
  @settings = settings
@@ -54,13 +55,49 @@ module VmShepherd
54
55
  }
55
56
  )
56
57
  when VmShepherd::AWS_IAAS_TYPE then
57
- ami_manager(vm_shepherd_config).deploy(path)
58
+ ami_manager.deploy(ami_file_path: path, vm_config: vm_shepherd_config.to_h)
58
59
  when VmShepherd::OPENSTACK_IAAS_TYPE then
59
60
  openstack_vm_manager(vm_shepherd_config).deploy(path, openstack_vm_options(vm_shepherd_config))
60
61
  end
61
62
  end
62
63
  end
63
64
 
65
+ def prepare_environment
66
+ unless valid_iaas_types.include?(settings.iaas_type)
67
+ fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
68
+ end
69
+ case settings.iaas_type
70
+ when VmShepherd::VCLOUD_IAAS_TYPE then
71
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
72
+ VmShepherd::VcloudManager.new(
73
+ {
74
+ url: vm_shepherd_config.creds.url,
75
+ organization: vm_shepherd_config.creds.organization,
76
+ user: vm_shepherd_config.creds.user,
77
+ password: vm_shepherd_config.creds.password,
78
+ },
79
+ vm_shepherd_config.vdc.name,
80
+ error_logger
81
+ ).prepare_environment
82
+ end
83
+ when VmShepherd::VSPHERE_IAAS_TYPE then
84
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
85
+ VmShepherd::VsphereManager.new(
86
+ vm_shepherd_config.vcenter_creds.ip,
87
+ vm_shepherd_config.vcenter_creds.username,
88
+ vm_shepherd_config.vcenter_creds.password,
89
+ vm_shepherd_config.vsphere.datacenter,
90
+ ).prepare_environment
91
+ end
92
+ when VmShepherd::AWS_IAAS_TYPE then
93
+ ami_manager.prepare_environment(settings.vm_shepherd.env_config.json_file)
94
+ when VmShepherd::OPENSTACK_IAAS_TYPE then
95
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
96
+ openstack_vm_manager(vm_shepherd_config).prepare_environment
97
+ end
98
+ end
99
+ end
100
+
64
101
  def destroy
65
102
  unless valid_iaas_types.include?(settings.iaas_type)
66
103
  fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
@@ -86,7 +123,7 @@ module VmShepherd
86
123
  vm_shepherd_config.vsphere.datacenter,
87
124
  ).destroy(vm_shepherd_config.vm.ip, vm_shepherd_config.vsphere.resource_pool)
88
125
  when VmShepherd::AWS_IAAS_TYPE then
89
- ami_manager(vm_shepherd_config).destroy
126
+ ami_manager.destroy(vm_shepherd_config.to_h)
90
127
  when VmShepherd::OPENSTACK_IAAS_TYPE then
91
128
  openstack_vm_manager(vm_shepherd_config).destroy(openstack_vm_options(vm_shepherd_config))
92
129
  end
@@ -97,9 +134,9 @@ module VmShepherd
97
134
  unless valid_iaas_types.include?(settings.iaas_type)
98
135
  fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
99
136
  end
100
- vm_shepherd_configs(settings).each do |vm_shepherd_config|
101
- case settings.iaas_type
102
- when VmShepherd::VCLOUD_IAAS_TYPE then
137
+ case settings.iaas_type
138
+ when VmShepherd::VCLOUD_IAAS_TYPE then
139
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
103
140
  VmShepherd::VcloudManager.new(
104
141
  {
105
142
  url: vm_shepherd_config.creds.url,
@@ -110,7 +147,9 @@ module VmShepherd
110
147
  vm_shepherd_config.vdc.name,
111
148
  error_logger,
112
149
  ).clean_environment(vm_shepherd_config.vapp.product_names || [], vm_shepherd_config.vapp.product_catalog)
113
- when VmShepherd::VSPHERE_IAAS_TYPE then
150
+ end
151
+ when VmShepherd::VSPHERE_IAAS_TYPE then
152
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
114
153
  VmShepherd::VsphereManager.new(
115
154
  vm_shepherd_config.vcenter_creds.ip,
116
155
  vm_shepherd_config.vcenter_creds.username,
@@ -121,11 +160,13 @@ module VmShepherd
121
160
  datastores: vm_shepherd_config.cleanup.datastores,
122
161
  datastore_folders_to_clean: vm_shepherd_config.cleanup.datastore_folders_to_clean,
123
162
  )
124
- when VmShepherd::AWS_IAAS_TYPE then
125
- ami_manager(vm_shepherd_config).clean_environment
126
- when VmShepherd::OPENSTACK_IAAS_TYPE then
163
+ end
164
+ when VmShepherd::AWS_IAAS_TYPE then
165
+ ami_manager.clean_environment
166
+ when VmShepherd::OPENSTACK_IAAS_TYPE then
167
+ vm_shepherd_configs(settings).each do |vm_shepherd_config|
127
168
  openstack_vm_manager(vm_shepherd_config).clean_environment
128
- end
169
+ end
129
170
  end
130
171
  end
131
172
 
@@ -153,16 +194,16 @@ module VmShepherd
153
194
  }
154
195
  end
155
196
 
156
- def ami_manager(vm_shepherd_config)
157
- VmShepherd::AwsManager.new(
158
- aws_access_key: vm_shepherd_config.aws_access_key,
159
- aws_secret_key: vm_shepherd_config.aws_secret_key,
160
- ssh_key_name: vm_shepherd_config.ssh_key_name,
161
- security_group_id: vm_shepherd_config.security_group,
162
- public_subnet_id: vm_shepherd_config.public_subnet_id,
163
- private_subnet_id: vm_shepherd_config.private_subnet_id,
164
- elastic_ip_id: vm_shepherd_config.elastic_ip_id,
165
- vm_name: vm_shepherd_config.vm_name,
197
+ def ami_manager
198
+ @ami_manager ||= VmShepherd::AwsManager.new(
199
+ {
200
+ stack_name: settings.vm_shepherd.env_config.stack_name,
201
+ aws_access_key: settings.vm_shepherd.env_config.aws_access_key,
202
+ aws_secret_key: settings.vm_shepherd.env_config.aws_secret_key,
203
+ json_file: settings.vm_shepherd.env_config.json_file,
204
+ parameters: settings.vm_shepherd.env_config.parameters_as_a_hash,
205
+ outputs: settings.vm_shepherd.env_config.outputs.to_h
206
+ }
166
207
  )
167
208
  end
168
209
 
@@ -188,7 +229,7 @@ module VmShepherd
188
229
  end
189
230
 
190
231
  def vm_shepherd_configs(settings)
191
- settings.vm_shepherd_configs || []
232
+ settings.vm_shepherd.vm_configs || []
192
233
  end
193
234
 
194
235
  def valid_iaas_types
@@ -24,6 +24,9 @@ module VmShepherd
24
24
  FileUtils.remove_entry_secure(tmpdir, force: true)
25
25
  end
26
26
 
27
+ def prepare_environment
28
+ end
29
+
27
30
  def destroy(vapp_names, catalog)
28
31
  delete_vapps(vapp_names)
29
32
  delete_catalog(catalog)
@@ -1,3 +1,3 @@
1
1
  module VmShepherd
2
- VERSION = '1.0.3'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
@@ -56,6 +56,9 @@ module VmShepherd
56
56
  end
57
57
  end
58
58
 
59
+ def prepare_environment
60
+ end
61
+
59
62
  def destroy(ip_address, resource_pool_name)
60
63
  vms = connection.serviceContent.searchIndex.FindAllByIp(ip: ip_address, vmSearch: true)
61
64
  vms = vms.select { |vm| resource_pool_name == vm.resourcePool.name } if resource_pool_name
@@ -120,20 +123,26 @@ module VmShepherd
120
123
  end
121
124
 
122
125
  def delete_folder_and_vms(folder_name)
123
- return unless (folder = datacenter.vmFolder.traverse(folder_name))
126
+ 3.times do |attempt|
127
+ break unless (folder = datacenter.vmFolder.traverse(folder_name))
124
128
 
125
- find_vms(folder).each { |vm| power_off_vm(vm) }
129
+ find_vms(folder).each { |vm| power_off_vm(vm) }
126
130
 
127
- logger.info("BEGIN folder.destroy_task folder=#{folder_name}")
128
- folder.Destroy_Task.wait_for_completion
129
- logger.info("END folder.destroy_task folder=#{folder_name}")
130
-
131
- fail("#{folder_name.inspect} already exists") unless datacenter.vmFolder.traverse(folder_name).nil?
132
- rescue RbVmomi::Fault => e
133
- logger.info("ERROR folder.destroy_task folder=#{folder_name} #{e.inspect}")
134
- raise
131
+ begin
132
+ logger.info("BEGIN folder.destroy_task folder=#{folder_name} attempt ##{attempt}")
133
+ folder.Destroy_Task.wait_for_completion
134
+ logger.info("END folder.destroy_task folder=#{folder_name}")
135
+ fail("#{folder_name.inspect} already exists") unless datacenter.vmFolder.traverse(folder_name).nil?
136
+ rescue RbVmomi::Fault => e
137
+ logger.info("ERROR folder.destroy_task folder=#{folder_name} #{e.inspect}")
138
+ sleep 10
139
+ # give up if it's the 3rd attempt
140
+ raise if attempt == 2
141
+ end
142
+ end
135
143
  end
136
144
 
145
+
137
146
  def find_vms(folder)
138
147
  vms = folder.childEntity.grep(RbVmomi::VIM::VirtualMachine)
139
148
  vms << folder.childEntity.grep(RbVmomi::VIM::Folder).map { |child| find_vms(child) }
@@ -1,19 +1,18 @@
1
1
  iaas_type: aws
2
2
 
3
- vm_shepherd_configs:
4
- - aws_access_key: aws-access-key
5
- aws_secret_key: aws-secret-key
6
- ssh_key_name: ssh-key-name
7
- security_group: security-group-id
8
- public_subnet_id: public-subnet-id
9
- private_subnet_id: private-subnet-id
10
- elastic_ip_id: elastic-ip-id
11
- vm_name: vm-name
12
- - aws_access_key: aws-access-key-2
13
- aws_secret_key: aws-secret-key-2
14
- ssh_key_name: ssh-key-name-2
15
- security_group: security-group-id-2
16
- public_subnet_id: public-subnet-id-2
17
- private_subnet_id: private-subnet-id-2
18
- elastic_ip_id: elastic-ip-id-2
19
- vm_name: vm-name-2
3
+ vm_shepherd:
4
+ env_config:
5
+ stack_name: aws-stack-name
6
+ aws_access_key: aws-access-key
7
+ aws_secret_key: aws-secret-key
8
+ json_file: cloudformation.json
9
+ parameters:
10
+ key_pair_name: key_pair_name
11
+ outputs:
12
+ ssh_key_name: ssh-key-name
13
+ security_group: security-group-id
14
+ public_subnet_id: public-subnet-id
15
+ private_subnet_id: private-subnet-id
16
+ vm_configs:
17
+ - vm_name: vm-name
18
+ - vm_name: vm-name-2
@@ -1,36 +1,37 @@
1
1
  iaas_type: openstack
2
- vm_shepherd_configs:
3
- - creds:
4
- auth_url: 'http://example.com/version/tokens'
5
- username: 'username'
6
- api_key: 'api-key'
7
- tenant: 'tenant'
8
- vm:
9
- name: 'some-vm-name'
10
- flavor_parameters:
11
- min_disk_size: 150
12
- network_name: 'some-network'
13
- key_name: 'some-key'
14
- security_group_names:
15
- - 'security-group-A'
16
- - 'security-group-B'
17
- - 'security-group-C'
18
- public_ip: 198.11.195.5
19
- private_ip: 192.168.100.100
20
- - creds:
21
- auth_url: 'http://example.com/version/tokens-2'
22
- username: 'username-2'
23
- api_key: 'api-key-2'
24
- tenant: 'tenant-2'
25
- vm:
26
- name: 'some-vm-name-2'
27
- flavor_parameters:
28
- min_disk_size: 152
29
- network_name: 'some-network-2'
30
- key_name: 'some-key-2'
31
- security_group_names:
32
- - 'security-group-A-2'
33
- - 'security-group-B-2'
34
- - 'security-group-C-2'
35
- public_ip: 198.11.195.5-2
36
- private_ip: 192.168.100.100-2
2
+ vm_shepherd:
3
+ vm_configs:
4
+ - creds:
5
+ auth_url: 'http://example.com/version/tokens'
6
+ username: 'username'
7
+ api_key: 'api-key'
8
+ tenant: 'tenant'
9
+ vm:
10
+ name: 'some-vm-name'
11
+ flavor_parameters:
12
+ min_disk_size: 150
13
+ network_name: 'some-network'
14
+ key_name: 'some-key'
15
+ security_group_names:
16
+ - 'security-group-A'
17
+ - 'security-group-B'
18
+ - 'security-group-C'
19
+ public_ip: 198.11.195.5
20
+ private_ip: 192.168.100.100
21
+ - creds:
22
+ auth_url: 'http://example.com/version/tokens-2'
23
+ username: 'username-2'
24
+ api_key: 'api-key-2'
25
+ tenant: 'tenant-2'
26
+ vm:
27
+ name: 'some-vm-name-2'
28
+ flavor_parameters:
29
+ min_disk_size: 152
30
+ network_name: 'some-network-2'
31
+ key_name: 'some-key-2'
32
+ security_group_names:
33
+ - 'security-group-A-2'
34
+ - 'security-group-B-2'
35
+ - 'security-group-C-2'
36
+ public_ip: 198.11.195.5-2
37
+ private_ip: 192.168.100.100-2