vm_shepherd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,166 @@
1
+ require 'vm_shepherd/vapp_manager/deployer'
2
+ require 'vm_shepherd/vapp_manager/destroyer'
3
+ require 'vm_shepherd/ova_manager/deployer'
4
+ require 'vm_shepherd/ova_manager/destroyer'
5
+ require 'vm_shepherd/ami_manager'
6
+
7
+ module VmShepherd
8
+ class Shepherd
9
+ VCLOUD_IAAS_TYPE = 'vcloud'.freeze
10
+ VSPHERE_IAAS_TYPE = 'vsphere'.freeze
11
+ AWS_IAAS_TYPE = 'aws'.freeze
12
+ VSPHERE_TEMPLATE_PREFIX = 'tpl'.freeze
13
+
14
+ class InvalidIaas < StandardError
15
+
16
+ end
17
+
18
+ def initialize(settings:)
19
+ @settings = settings
20
+ end
21
+
22
+ def deploy(path:)
23
+ case settings.iaas_type
24
+ when VCLOUD_IAAS_TYPE then
25
+ vcloud_deployer.deploy(
26
+ path,
27
+ vcloud_deploy_options,
28
+ )
29
+ when VSPHERE_IAAS_TYPE then
30
+ vsphere_deployer.deploy(
31
+ VSPHERE_TEMPLATE_PREFIX,
32
+ path,
33
+ vsphere_deploy_options,
34
+ )
35
+ when AWS_IAAS_TYPE then
36
+ ami_manager.deploy(path)
37
+ else
38
+ fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
39
+ end
40
+ end
41
+
42
+ def destroy
43
+ case settings.iaas_type
44
+ when VCLOUD_IAAS_TYPE then
45
+ VmShepherd::VappManager::Destroyer.new(
46
+ {
47
+ url: settings.vapp_deployer.creds.url,
48
+ organization: settings.vapp_deployer.creds.organization,
49
+ user: settings.vapp_deployer.creds.user,
50
+ password: settings.vapp_deployer.creds.password,
51
+ },
52
+ {
53
+ vdc: settings.vapp_deployer.vdc.name,
54
+ catalog: settings.vapp_deployer.vdc.catalog,
55
+ },
56
+ Logger.new(STDOUT).tap { |l| l.level = Logger::Severity::ERROR }
57
+ ).destroy(settings.vapp_deployer.vapp.name)
58
+ when VSPHERE_IAAS_TYPE then
59
+ VmShepherd::OvaManager::Destroyer.new(
60
+ settings.vm_deployer.vsphere.datacenter,
61
+ {
62
+ host: settings.vm_deployer.vcenter_creds.ip,
63
+ user: settings.vm_deployer.vcenter_creds.username,
64
+ password: settings.vm_deployer.vcenter_creds.password,
65
+ }
66
+ ).clean_folder(settings.vm_deployer.vsphere.folder)
67
+ when AWS_IAAS_TYPE then
68
+ ami_manager.destroy
69
+ else
70
+ fail(InvalidIaas, "Unknown IaaS type: #{settings.iaas_type.inspect}")
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :settings
77
+
78
+ def logger
79
+ Logger.new(STDOUT).tap do |lggr|
80
+ lggr.level = Logger::Severity::ERROR
81
+ end
82
+ end
83
+
84
+ def debug_logger
85
+ Logger.new(STDOUT).tap do |lggr|
86
+ lggr.level = Logger::Severity::DEBUG
87
+ end
88
+ end
89
+
90
+ def vcloud_deployer
91
+ creds = settings.vapp_deployer.creds
92
+ vdc = settings.vapp_deployer.vdc
93
+ VmShepherd::VappManager::Deployer.new(
94
+ {
95
+ url: creds.url,
96
+ organization: creds.organization,
97
+ user: creds.user,
98
+ password: creds.password,
99
+ },
100
+ {
101
+ vdc: vdc.name,
102
+ catalog: vdc.catalog,
103
+ network: vdc.network,
104
+ },
105
+ debug_logger
106
+ )
107
+ end
108
+
109
+ def vcloud_deploy_options
110
+ vm = settings.vapp_deployer.vapp
111
+ {
112
+ name: vm.name,
113
+ ip: vm.ip,
114
+ gateway: vm.gateway,
115
+ netmask: vm.netmask,
116
+ dns: vm.dns,
117
+ ntp: vm.ntp,
118
+ }
119
+ end
120
+
121
+ def vsphere_deployer
122
+ vcenter_creds = settings.vm_deployer.vcenter_creds
123
+ vsphere = settings.vm_deployer.vsphere
124
+ VmShepherd::OvaManager::Deployer.new(
125
+ {
126
+ host: vcenter_creds.ip,
127
+ user: vcenter_creds.username,
128
+ password: vcenter_creds.password,
129
+ },
130
+ {
131
+ datacenter: vsphere.datacenter,
132
+ cluster: vsphere.cluster,
133
+ resource_pool: vsphere.resource_pool,
134
+ datastore: vsphere.datastore,
135
+ network: vsphere.network,
136
+ folder: vsphere.folder,
137
+ }
138
+ )
139
+ end
140
+
141
+ def vsphere_deploy_options
142
+ vm = settings.vm_deployer.vm
143
+ {
144
+ ip: vm.ip,
145
+ gateway: vm.gateway,
146
+ netmask: vm.netmask,
147
+ dns: vm.dns,
148
+ ntp_servers: vm.ntp_servers,
149
+ }
150
+ end
151
+
152
+ def ami_manager
153
+ vm_deployer = settings.vm_deployer
154
+ VmShepherd::AmiManager.new(
155
+ aws_access_key: vm_deployer.aws_access_key,
156
+ aws_secret_key: vm_deployer.aws_secret_key,
157
+ ssh_key_name: vm_deployer.ssh_key_name,
158
+ security_group_id: vm_deployer.security_group,
159
+ public_subnet_id: vm_deployer.public_subnet_id,
160
+ private_subnet_id: vm_deployer.private_subnet_id,
161
+ elastic_ip_id: vm_deployer.elastic_ip_id,
162
+ vm_name: vm_deployer.vm_name,
163
+ )
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,151 @@
1
+ require 'tmpdir'
2
+ require 'fileutils'
3
+ require 'ruby_vcloud_sdk'
4
+
5
+ module VmShepherd
6
+ module VappManager
7
+ class Deployer
8
+ def initialize(login_info, location, logger)
9
+ @login_info = login_info
10
+ @location = location
11
+ @logger = logger
12
+ raise 'VDC must be set' unless @location[:vdc]
13
+ raise 'Catalog must be set' unless @location[:catalog]
14
+ raise 'Network must be set' unless @location[:network]
15
+ end
16
+
17
+ def deploy(vapp_template_tar_path, vapp_config)
18
+ tmpdir = Dir.mktmpdir
19
+
20
+ check_vapp_status(vapp_config)
21
+
22
+ untar_vapp_template_tar(File.expand_path(vapp_template_tar_path), tmpdir)
23
+
24
+ vapp = deploy_vapp(tmpdir, vapp_config)
25
+ reconfigure_vm(vapp, vapp_config)
26
+ vapp.power_on
27
+ ensure
28
+ FileUtils.remove_entry_secure(tmpdir, force: true)
29
+ end
30
+
31
+ private
32
+
33
+ def check_vapp_status(vapp_config)
34
+ log('Checking for existing VM') do
35
+ ip = vapp_config[:ip]
36
+ raise "VM exists at #{ip}" if system("ping -c 5 #{ip}")
37
+ end
38
+ end
39
+
40
+ def untar_vapp_template_tar(vapp_template_tar_path, dir)
41
+ log("Untarring #{vapp_template_tar_path}") do
42
+ system_or_exit("cd #{dir} && tar xfv '#{vapp_template_tar_path}'")
43
+ end
44
+ end
45
+
46
+ def client
47
+ @client ||= VCloudSdk::Client.new(
48
+ @login_info[:url],
49
+ "#{@login_info[:user]}@#{@login_info[:organization]}",
50
+ @login_info[:password],
51
+ {},
52
+ @logger,
53
+ )
54
+ end
55
+
56
+ def deploy_vapp(ovf_dir, vapp_config)
57
+ # setup the catalog
58
+ client.delete_catalog_by_name(@location[:catalog]) if client.catalog_exists?(@location[:catalog])
59
+ catalog = client.create_catalog(@location[:catalog])
60
+
61
+ # upload template and instantiate vapp
62
+ catalog.upload_vapp_template(@location[:vdc], vapp_config[:name], ovf_dir)
63
+
64
+ # instantiate template
65
+ network_config = VCloudSdk::NetworkConfig.new(@location[:network], 'Network 1')
66
+ catalog.instantiate_vapp_template(
67
+ vapp_config[:name], @location[:vdc], vapp_config[:name], nil, nil, network_config)
68
+ rescue => e
69
+ @logger.error(e.http_body) if e.respond_to?(:http_body)
70
+ raise e
71
+ end
72
+
73
+ def reconfigure_vm(vapp, vapp_config)
74
+ vm = vapp.find_vm_by_name(vapp_config[:name])
75
+ vm.product_section_properties = build_properties(vapp_config)
76
+ vm
77
+ end
78
+
79
+ def build_properties(vapp_config)
80
+ [
81
+ {
82
+ 'type' => 'string',
83
+ 'key' => 'gateway',
84
+ 'value' => vapp_config[:gateway],
85
+ 'password' => 'false',
86
+ 'userConfigurable' => 'true',
87
+ 'Label' => 'Default Gateway',
88
+ 'Description' => 'The default gateway address for the VM network. Leave blank if DHCP is desired.'
89
+ },
90
+ {
91
+ 'type' => 'string',
92
+ 'key' => 'DNS',
93
+ 'value' => vapp_config[:dns],
94
+ 'password' => 'false',
95
+ 'userConfigurable' => 'true',
96
+ 'Label' => 'DNS',
97
+ 'Description' => 'The domain name servers for the VM (comma separated). Leave blank if DHCP is desired.',
98
+ },
99
+ {
100
+ 'type' => 'string',
101
+ 'key' => 'ntp_servers',
102
+ 'value' => vapp_config[:ntp],
103
+ 'password' => 'false',
104
+ 'userConfigurable' => 'true',
105
+ 'Label' => 'NTP Servers',
106
+ 'Description' => 'Comma-delimited list of NTP servers'
107
+ },
108
+ {
109
+ 'type' => 'string',
110
+ 'key' => 'admin_password',
111
+ 'value' => 'tempest',
112
+ 'password' => 'true',
113
+ 'userConfigurable' => 'true',
114
+ 'Label' => 'Admin Password',
115
+ 'Description' => 'This password is used to SSH into the VM. The username is "tempest".',
116
+ },
117
+ {
118
+ 'type' => 'string',
119
+ 'key' => 'ip0',
120
+ 'value' => vapp_config[:ip],
121
+ 'password' => 'false',
122
+ 'userConfigurable' => 'true',
123
+ 'Label' => 'IP Address',
124
+ 'Description' => 'The IP address for the VM. Leave blank if DHCP is desired.',
125
+ },
126
+ {
127
+ 'type' => 'string',
128
+ 'key' => 'netmask0',
129
+ 'value' => vapp_config[:netmask],
130
+ 'password' => 'false',
131
+ 'userConfigurable' => 'true',
132
+ 'Label' => 'Netmask',
133
+ 'Description' => 'The netmask for the VM network. Leave blank if DHCP is desired.'
134
+ }
135
+ ]
136
+ end
137
+
138
+ def log(title, &blk)
139
+ @logger.debug "--- Begin: #{title.inspect} @ #{DateTime.now}"
140
+ blk.call
141
+ @logger.debug "--- End: #{title.inspect} @ #{DateTime.now}"
142
+ end
143
+
144
+ def system_or_exit(command)
145
+ log(command) do
146
+ system(command) || raise("Error executing: #{command.inspect}")
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,46 @@
1
+ require 'ruby_vcloud_sdk'
2
+
3
+ module VmShepherd
4
+ module VappManager
5
+ class Destroyer
6
+ def initialize(login_info, location, logger)
7
+ @login_info = login_info
8
+ @location = location
9
+ @logger = logger
10
+ end
11
+
12
+ def destroy(vapp_name)
13
+ delete_vapp(vapp_name)
14
+ delete_catalog
15
+ end
16
+
17
+ private
18
+
19
+ def client
20
+ @client ||= VCloudSdk::Client.new(
21
+ @login_info[:url],
22
+ "#{@login_info[:user]}@#{@login_info[:organization]}",
23
+ @login_info[:password],
24
+ {},
25
+ @logger,
26
+ )
27
+ end
28
+
29
+ def vdc
30
+ @vdc ||= client.find_vdc_by_name(@location[:vdc])
31
+ end
32
+
33
+ def delete_vapp(vapp_name)
34
+ vapp = vdc.find_vapp_by_name(vapp_name)
35
+ vapp.power_off
36
+ vapp.delete
37
+ rescue VCloudSdk::ObjectNotFoundError => e
38
+ @logger.debug "Could not delete vapp '#{vapp_name}': #{e.inspect}"
39
+ end
40
+
41
+ def delete_catalog
42
+ client.delete_catalog_by_name(@location[:catalog]) if client.catalog_exists?(@location[:catalog])
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module VmShepherd
2
+ VERSION = '0.0.1'.freeze
3
+ end
@@ -0,0 +1,11 @@
1
+ iaas_type: aws
2
+
3
+ vm_deployer:
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
@@ -0,0 +1 @@
1
+ iaas_type: unknown
@@ -0,0 +1,19 @@
1
+ iaas_type: vcloud
2
+ vapp_deployer:
3
+ creds:
4
+ url: VAPP_URL
5
+ organization: VAPP_ORGANIZATION
6
+ user: VAPP_USERNAME
7
+ password: VAPP_PASSWORD
8
+ vdc:
9
+ name: VDC_NAME
10
+ catalog: VDC_CATALOG
11
+ network: VDC_NETWORK
12
+ vapp:
13
+ name: VAPP_NAME
14
+ ip: VAPP_IP
15
+ public_ip:
16
+ gateway: VAPP_GATEWAY
17
+ netmask: VAPP_NETMASK
18
+ dns: VAPP_DNS
19
+ ntp: VAPP_NTP
@@ -0,0 +1,20 @@
1
+ iaas_type: vsphere
2
+ vm_deployer:
3
+ vcenter_creds:
4
+ ip: OVA_URL
5
+ username: OVA_ORGANIZATION
6
+ password: OVA_PASSWORD
7
+ vsphere:
8
+ host:
9
+ datacenter: VSPHERE_DATACENTER
10
+ cluster: VSPHERE_CLUSTER
11
+ network: VSPHERE_NETWORK
12
+ resource_pool: VSPHERE_RESOURCE_POOL
13
+ datastore: VSPHERE_DATASTORE
14
+ folder: VSPHERE_FOLDER
15
+ vm:
16
+ ip: OVA_IP
17
+ gateway: OVA_GATEWAY
18
+ netmask: OVA_NETMASK
19
+ dns: OVA_DNS
20
+ ntp_servers: OVA_NTP
@@ -0,0 +1,28 @@
1
+ SPEC_ROOT = File.expand_path(__dir__)
2
+
3
+ $LOAD_PATH << SPEC_ROOT
4
+
5
+ RSpec.configure do |config|
6
+ config.expect_with :rspec do |expectations|
7
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
+ end
9
+
10
+ config.mock_with :rspec do |mocks|
11
+ mocks.verify_partial_doubles = true
12
+ end
13
+
14
+ config.filter_run :focus
15
+ config.run_all_when_everything_filtered = true
16
+ config.disable_monkey_patching!
17
+ # config.warnings = true
18
+
19
+ if config.files_to_run.one?
20
+ config.default_formatter = 'doc'
21
+ end
22
+
23
+ config.profile_examples = 3
24
+
25
+ config.order = :random
26
+
27
+ Kernel.srand config.seed #this allows you to use `--seed` to deterministically reproduce failures
28
+ end