vm_shepherd 1.0.3 → 1.1.0

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