gogetit 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+ require 'gogetit'
3
+
4
+ module Gogetit
5
+ class CLI < Thor
6
+ package_name 'Gogetit'
7
+
8
+ desc 'list', 'List containers and instances, running currently.'
9
+ def list
10
+ puts "Listing LXD containers on #{Gogetit.config[:lxd][:url]}.."
11
+ system("lxc list #{Gogetit.config[:lxd][:name]}:")
12
+ puts ''
13
+ puts "Listing KVM domains on #{Gogetit.config[:libvirt][:url]}.."
14
+ system("virsh -c #{Gogetit.config[:libvirt][:url]} list --all")
15
+ end
16
+
17
+ desc 'create (TYPE) NAME', 'Create either a container or KVM domain.'
18
+ def create(type=nil, name)
19
+ case type
20
+ when 'lxd', nil
21
+ Gogetit.lxd.create(name)
22
+ when 'libvirt'
23
+ Gogetit.libvirt.create(name)
24
+ else
25
+ puts 'Invalid argument entered'
26
+ end
27
+ Gogetit.config[:default][:user] ||= ENV['USER']
28
+ puts "ssh #{Gogetit.config[:default][:user]}@#{name}"
29
+ end
30
+
31
+ desc 'destroy NAME', 'Destroy either a container or KVM domain.'
32
+ def destroy(name)
33
+ type = Gogetit.get_provider_of(name)
34
+ if type
35
+ case type
36
+ when 'lxd', nil
37
+ Gogetit.lxd.destroy(name)
38
+ when 'libvirt'
39
+ Gogetit.libvirt.destroy(name)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Gogetit
2
+ VERSION = "0.1.16"
3
+ end
@@ -0,0 +1,119 @@
1
+ require 'maas/client'
2
+
3
+ module Gogetit
4
+ class GogetMAAS
5
+ attr_reader :config, :conn, :domain, :logger
6
+
7
+ def initialize(conf, logger)
8
+ @config = conf
9
+ @conn = Maas::Client::MaasClient.new(
10
+ config[:maas][:key],
11
+ config[:maas][:url]
12
+ )
13
+ @logger = logger
14
+ end
15
+
16
+ def get_domain
17
+ return @domain if @domain
18
+ logger.info("Calling <#{__method__.to_s}>")
19
+ @domain = conn.request(:get, ['domains'])[0]['name']
20
+ end
21
+
22
+ def machine_exists?(name)
23
+ logger.info("Calling <#{__method__.to_s}>")
24
+ conn.request(:get, ['machines']).each do |m|
25
+ return true if m['hostname'] == name
26
+ end
27
+ false
28
+ end
29
+
30
+ def dnsresource_exists?(name)
31
+ logger.info("Calling <#{__method__.to_s}>")
32
+ conn.request(:get, ['dnsresources']).each do |item|
33
+ return true if item['fqdn'] == name + '.' + get_domain
34
+ end
35
+ false
36
+ end
37
+
38
+ def domain_name_exists?(name)
39
+ return true if dnsresource_exists?(name) or machine_exists?(name)
40
+ end
41
+
42
+ def ipaddresses(op = nil, params = nil)
43
+ case op
44
+ when nil
45
+ conn.request(:get, ['ipaddresses'])
46
+ when 'reserve'
47
+ # sample = {
48
+ # 'subnet' => '10.1.2.0/24',
49
+ # 'ip' => '10.1.2.8',
50
+ # 'hostname' => 'hostname',
51
+ # 'mac' => 'blahblah'
52
+ # }
53
+ default_param = { 'op' => op }
54
+ conn.request(:post, ['ipaddresses'], default_param.merge!(params))
55
+ when 'release'
56
+ # Gogetit.maas.ipaddresses('release', {'ip' => '10.1.2.8'})
57
+ # sample = {
58
+ # 'ip' => '10.1.2.8',
59
+ # 'hostname' => 'hostname',
60
+ # 'mac' => 'blahblah'
61
+ # }
62
+ default_param = { 'op' => op }
63
+ conn.request(:post, ['ipaddresses'], default_param.merge!(params))
64
+ end
65
+ end
66
+
67
+ def delete_dns_record(name)
68
+ logger.info("Calling <#{__method__.to_s}>")
69
+ id = nil
70
+ conn.request(:get, ['dnsresources']).each do |item|
71
+ if item['fqdn'] == name + '.' + get_domain
72
+ id = item['id']
73
+ end
74
+ end
75
+
76
+ if ! id.nil?
77
+ conn.request(:delete, ['dnsresources', id.to_s])
78
+ else
79
+ logger.warn('No such record found.')
80
+ end
81
+ end
82
+
83
+ def refresh_pods
84
+ logger.info("Calling <#{__method__.to_s}>")
85
+ pod_id = conn.request(:get, ['pods'])
86
+ pod_id.each do |pod|
87
+ conn.request(:post, ['pods', pod['id']], { 'op' => 'refresh' } )
88
+ end
89
+ end
90
+
91
+ def get_system_id(name)
92
+ logger.info("Calling <#{__method__.to_s}>")
93
+ conn.request(:get, ['machines']).each do |m|
94
+ return m['system_id'] if m['hostname'] == name
95
+ end
96
+ nil
97
+ end
98
+
99
+ def wait_until_state(system_id, state)
100
+ logger.info("Calling <#{__method__.to_s}> for being #{state}")
101
+ until conn.request(:get, ['machines', system_id])['status_name'] == state
102
+ sleep 3
103
+ end
104
+ end
105
+
106
+ def get_machine_state(system_id)
107
+ logger.info("Calling <#{__method__.to_s}>")
108
+ conn.request(:get, ['machines']).each do |m|
109
+ return m['status_name'] if m['system_id'] == system_id
110
+ end
111
+ false
112
+ end
113
+
114
+ def change_hostname(system_id, hostname)
115
+ logger.info("Calling <#{__method__.to_s}>")
116
+ conn.request(:put, ['machines', system_id], { 'hostname' => hostname })
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,56 @@
1
+ require 'logger'
2
+
3
+ module Gogetit
4
+ # It was just taken from below source. Thanks to clowder!
5
+ # https://gist.github.com/clowder/3639600
6
+ class MultiLogger
7
+ attr_reader :level
8
+
9
+ def initialize(args={})
10
+ @level = args[:level] || Logger::Severity::INFO
11
+ @loggers = []
12
+
13
+ Array(args[:loggers]).each { |logger| add_logger(logger) }
14
+ end
15
+
16
+ def add_logger(logger)
17
+ logger.level = level
18
+ @loggers << logger
19
+ end
20
+
21
+ def level=(level)
22
+ @level = level
23
+ @loggers.each { |logger| logger.level = level }
24
+ end
25
+
26
+ def datetime_format=(format)
27
+ @loggers.each { |logger| logger.datetime_format = format }
28
+ end
29
+
30
+ def formatter=(format)
31
+ @loggers.each { |logger| logger.formatter = format }
32
+ end
33
+
34
+ def progname=(name)
35
+ @loggers.each { |logger| logger.progname = name }
36
+ end
37
+
38
+ def close
39
+ @loggers.map(&:close)
40
+ end
41
+
42
+ def add(level, *args)
43
+ @loggers.each { |logger| logger.add(level, args) }
44
+ end
45
+
46
+ Logger::Severity.constants.each do |level|
47
+ define_method(level.downcase) do |*args|
48
+ @loggers.each { |logger| logger.send(level.downcase, args) }
49
+ end
50
+
51
+ define_method("#{ level.downcase }?".to_sym) do
52
+ @level <= Logger::Severity.const_get(level)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,260 @@
1
+ require 'libvirt'
2
+ require 'securerandom'
3
+ require 'oga'
4
+ require 'rexml/document'
5
+ require 'util'
6
+
7
+ module Gogetit
8
+ class GogetLibvirt
9
+ include Gogetit::Util
10
+
11
+ attr_reader :config, :conn, :maas, :logger
12
+
13
+ def initialize(conf, maas, logger)
14
+ @config = conf
15
+ @conn = Libvirt::open(config[:libvirt][:url])
16
+ @maas = maas
17
+ @logger = logger
18
+ end
19
+
20
+ def get_domain_list
21
+ logger.info("Calling <#{__method__.to_s}>")
22
+ domains = []
23
+ conn.list_all_domains.each do |d|
24
+ domains << d.name
25
+ end
26
+ domains
27
+ end
28
+
29
+ def domain_exists?(name)
30
+ logger.info("Calling <#{__method__.to_s}>")
31
+ get_domain_list.each do |d|
32
+ return true if d == name
33
+ end
34
+ false
35
+ end
36
+
37
+ def get_mac_addr(domain_name)
38
+ logger.info("Calling <#{__method__.to_s}>")
39
+ Oga.parse_xml(conn.lookup_domain_by_name(domain_name).xml_desc)
40
+ .at_xpath('domain/devices/interface[1]/mac')
41
+ .attribute('address')
42
+ .value
43
+ end
44
+
45
+ # subject.create(name: 'test01')
46
+ def create(name, conf_file = nil)
47
+ logger.info("Calling <#{__method__.to_s}>")
48
+ if maas.domain_name_exists?(name) or domain_exists?(name)
49
+ puts "Domain #{name} already exists! Please check both on MAAS and libvirt."
50
+ return false
51
+ end
52
+
53
+ conf_file ||= config[:default_provider_conf_file]
54
+ domain = symbolize_keys(YAML.load_file(conf_file))
55
+ domain[:name] = name
56
+ domain[:uuid] = SecureRandom.uuid
57
+
58
+ dom = conn.define_domain_xml(define_domain(domain))
59
+ maas.refresh_pods
60
+
61
+ system_id = maas.get_system_id(domain[:name])
62
+ maas.wait_until_state(system_id, 'Ready')
63
+ logger.info("Calling to deploy...")
64
+ maas.conn.request(:post, ['machines', system_id], {'op' => 'deploy'})
65
+ maas.wait_until_state(system_id, 'Deployed')
66
+ logger.info("#{domain[:name]} has been created.")
67
+ true
68
+ end
69
+
70
+ def destroy(name)
71
+ logger.info("Calling <#{__method__.to_s}>")
72
+ system_id = maas.get_system_id(name)
73
+ if maas.machine_exists?(name)
74
+ if maas.get_machine_state(system_id) == 'Deployed'
75
+ logger.info("Calling to release...")
76
+ maas.conn.request(:post, ['machines', system_id], {'op' => 'release'})
77
+ maas.wait_until_state(system_id, 'Ready')
78
+ end
79
+ maas.conn.request(:delete, ['machines', system_id])
80
+ end
81
+
82
+ pools = []
83
+ conn.list_storage_pools.each do |name|
84
+ pools << self.conn.lookup_storage_pool_by_name(name)
85
+ end
86
+
87
+ dom = conn.lookup_domain_by_name(name)
88
+ dom.destroy if dom.active?
89
+ Oga.parse_xml(dom.xml_desc).xpath('domain/devices/disk/source').each do |d|
90
+ pool_path = d.attribute('file').value.split('/')[0..2].join('/')
91
+ pools.each do |p|
92
+ if Oga.parse_xml(p.xml_desc).at_xpath('pool/target/path').inner_text == pool_path
93
+ logger.info("Deleting volume in #{p.name} pool.")
94
+ p.lookup_volume_by_name(d.attribute('file').value.split('/')[3]).delete
95
+ end
96
+ end
97
+ end
98
+ dom.undefine
99
+
100
+ maas.refresh_pods
101
+ logger.info("#{name} has been destroyed.")
102
+ true
103
+ end
104
+
105
+ def define_domain(domain)
106
+ logger.info("Calling <#{__method__.to_s}>")
107
+ template = File.read(config[:lib_dir] + '/template/domain.xml')
108
+ doc = Oga.parse_xml(template)
109
+
110
+ name = domain[:name]
111
+ doc.at_xpath('domain/name').inner_text = name
112
+ uuid = domain[:uuid]
113
+ doc.at_xpath('domain/uuid').inner_text = uuid
114
+ vcpu = domain[:vcpu].to_s
115
+ doc.at_xpath('domain/vcpu').inner_text = vcpu
116
+ memory = domain[:memory].to_s
117
+ doc.at_xpath('domain/memory').inner_text = memory
118
+ doc.at_xpath('domain/currentMemory').inner_text = memory
119
+
120
+ doc = define_volumes(doc, domain)
121
+ doc = add_nic(doc, domain[:nic])
122
+
123
+ #print_xml(doc)
124
+ #volumes.each do |v|
125
+ # print_xml(v)
126
+ #end
127
+
128
+ return Oga::XML::Generator.new(doc).to_xml
129
+ end
130
+
131
+ def print_xml(doc)
132
+ logger.info("Calling <#{__method__.to_s}>")
133
+ output = REXML::Document.new(Oga::XML::Generator.new(doc).to_xml)
134
+ formatter = REXML::Formatters::Pretty.new
135
+ formatter.compact = true
136
+ formatter.write(output, $stdout)
137
+ end
138
+
139
+ def get_pool_path(pool)
140
+ logger.info("Calling <#{__method__.to_s}>")
141
+ path = nil
142
+ conn.list_all_storage_pools.each do |p|
143
+ if p.name == pool
144
+ pool_doc = Oga.parse_xml(p.xml_desc)
145
+ path = pool_doc.at_xpath('pool/target/path').inner_text
146
+ end
147
+ end
148
+
149
+ if path
150
+ return path
151
+ else
152
+ raise 'No such pool found.'
153
+ end
154
+ end
155
+
156
+ def define_volumes(document, domain)
157
+ logger.info("Calling <#{__method__.to_s}>")
158
+ disk_template = File.read(config[:lib_dir] + '/template/disk.xml')
159
+ disk_doc = Oga.parse_xml(disk_template)
160
+ volume_template = File.read(config[:lib_dir] + '/template/volume.xml')
161
+ volume_doc = Oga.parse_xml(volume_template)
162
+
163
+ defined_volumes = []
164
+
165
+ # For root volume
166
+ pool_path = get_pool_path(domain[:disk][:root][:pool])
167
+ volume_name = "#{domain[:name]}_root_sda.qcow2"
168
+ volume_file = pool_path + "/" + volume_name
169
+ disk_doc.at_xpath('disk/source').attribute('file').value = volume_file
170
+ document.at_xpath('domain/devices').children << disk_doc.at_xpath('disk')
171
+
172
+ volume_doc.at_xpath('volume/name').inner_text = volume_name
173
+ volume_doc.at_xpath('volume/target/path').inner_text = volume_file
174
+ volume_doc.at_xpath('volume/capacity').inner_text = domain[:disk][:root][:capacity].to_s
175
+
176
+ create_volume(domain[:disk][:root][:pool], Oga::XML::Generator.new(volume_doc).to_xml)
177
+ defined_volumes << volume_doc
178
+
179
+ # For data(secondary) volumes
180
+ if domain[:disk][:data] != [] and domain[:disk][:data] != nil
181
+ disk_index = 98
182
+ domain[:disk][:data].each do |v|
183
+ pool_path = get_pool_path(v[:pool])
184
+ volume_index = "sd" + disk_index.chr
185
+ volume_name = "#{domain[:name]}_data_#{volume_index}.qcow2"
186
+ volume_file = pool_path + "/" + volume_name
187
+ disk_doc = Oga.parse_xml(disk_template)
188
+ disk_doc.at_xpath('disk/source').attribute('file').value = volume_file
189
+ disk_doc.at_xpath('disk/target').attribute('dev').value = volume_index
190
+ document.at_xpath('domain/devices').children << disk_doc.at_xpath('disk')
191
+
192
+ volume_doc = Oga.parse_xml(volume_template)
193
+ volume_doc.at_xpath('volume/name').inner_text = volume_name
194
+ volume_doc.at_xpath('volume/target/path').inner_text = volume_file
195
+ volume_doc.at_xpath('volume/capacity').inner_text = v[:capacity].to_s
196
+ create_volume(v[:pool], Oga::XML::Generator.new(volume_doc).to_xml)
197
+ defined_volumes << volume_doc
198
+ disk_index += 1
199
+ end
200
+ end
201
+
202
+ return document
203
+ end
204
+
205
+ def create_volume(pool_name, volume_doc)
206
+ logger.info("Calling <#{__method__.to_s}> to create volume in #{pool_name} pool.")
207
+ pool = conn.lookup_storage_pool_by_name(pool_name)
208
+ pool.create_volume_xml(volume_doc)
209
+ pool.refresh
210
+ end
211
+
212
+ def add_nic(document, nic_conf)
213
+ logger.info("Calling <#{__method__.to_s}>")
214
+ template = File.read(config[:lib_dir] + "/template/nic.xml")
215
+ doc = Oga.parse_xml(template)
216
+
217
+ nic_conf.each do |nic|
218
+ doc = Oga.parse_xml(template)
219
+ doc.at_xpath('interface/source').attribute('network').value = nic[:network]
220
+ doc.at_xpath('interface/source').attribute('portgroup').value = nic[:portgroup]
221
+ document.at_xpath('domain/devices').children << doc.at_xpath('interface')
222
+ end
223
+
224
+ document
225
+ end
226
+
227
+ #def generate_xml
228
+ # domain = Oga::XML::Element.new(name: 'domain')
229
+ # domain.add_attribute(Oga::XML::Attribute.new(name: "type", value: "kvm"))
230
+ # name = Oga::XML::Element.new(name: 'name')
231
+ # name.inner_text = 'ceph'
232
+ # domain.children << name
233
+ # doc = REXML::Document.new(Oga::XML::Generator.new(domain).to_xml)
234
+ # formatter = REXML::Formatters::Pretty.new
235
+ # formatter.compact = true
236
+ # formatter.write(doc, $stdout)
237
+
238
+ # disk = create_element(name: 'domain')
239
+ # disk.add_attribute(Oga::XML::Attribute.new(name: "type", value: "file"))
240
+ # disk.add_attribute(Oga::XML::Attribute.new(name: "device", value: "disk"))
241
+ # driver = create_element(name: 'domain')
242
+ # driver.add_attribute(Oga::XML::Attribute.new(name: "name", value: "qemu"))
243
+ # driver.add_attribute(Oga::XML::Attribute.new(name: "type", value: "qcow2"))
244
+ # source = create_element(name: 'source')
245
+ # source.add_attribute(Oga::XML::Attribute.new(name: "file", value: file))
246
+ # target = create_element(name: 'target')
247
+ # target.add_attribute(Oga::XML::Attribute.new(name: "dev", value: seq_name))
248
+ # target.add_attribute(Oga::XML::Attribute.new(name: "bus", value: "scsi"))
249
+
250
+ #end
251
+
252
+ #def create_element(name)
253
+ # Oga::XML::Element.new(name: name)
254
+ #end
255
+
256
+ #def add_attribute(element, hash)
257
+ # element.add_attribute(Oga::XML::Attribute.new(hash))
258
+ #end
259
+ end
260
+ end