gogetit 0.2.1 → 0.3.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 +4 -4
- data/README.md +37 -6
- data/lib/gogetit/cli.rb +18 -5
- data/lib/gogetit/config.rb +0 -18
- data/lib/gogetit/maas.rb +107 -12
- data/lib/gogetit/util.rb +19 -3
- data/lib/gogetit/version.rb +1 -1
- data/lib/providers/libvirt.rb +123 -10
- data/lib/providers/lxd.rb +195 -5
- data/lib/sample_conf/gogetit.yml +29 -2
- data/lib/sample_conf/lxd_profile.yaml +17 -0
- metadata +3 -4
- data/lib/sample_conf/ceph.yml +0 -17
- data/lib/sample_conf/default.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d5861321df7c324e2ed483329663114406a308b
|
4
|
+
data.tar.gz: 996bd6ae6cf0f34a38fb36f91abff1d94d06b34b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61507178c9a1ad0aedb2d969a5714f8d9c3197e75fd016aaa974a51f6d860e9e2d65bbdd45680ce72e48600db02dde295a47d626dafc902d482cda99b4795aeb
|
7
|
+
data.tar.gz: e517a9a7000d81f192a7e456e572933408d3a843536e3e7e7d8fb0f98c25323a517080632d71306be4b6ed6650c5f6aa1639b0e0091f66cdd7fa83df16c7f3cf
|
data/README.md
CHANGED
@@ -9,6 +9,12 @@ By using this, you will get to use them all together in automated and efficient
|
|
9
9
|
- Aware of Chef knife and its sub commands such as Vault to automate routine tasks.
|
10
10
|
- Being used by [kitchen-gogetkitchen](https://github.com/itisnotdone/kitchen-gogetkitchen) as a driver for Chef Test Kitchen.
|
11
11
|
|
12
|
+
## Limitations
|
13
|
+
- Network resource awareness is only provided by MAAS.
|
14
|
+
- Only LXD and Libvirt(KVM) are available as provider.
|
15
|
+
- Only IPv4 is available for IP assignment.
|
16
|
+
- It is tested only on Ubuntu 16.04 with OVS as virtual switch.
|
17
|
+
|
12
18
|
## Installation
|
13
19
|
|
14
20
|
### dependent packages
|
@@ -31,17 +37,41 @@ $ gem install gogetit --no-ri --no-rdoc
|
|
31
37
|
## Usage
|
32
38
|
```bash
|
33
39
|
gogetit list
|
34
|
-
gogetit create
|
35
|
-
gogetit create
|
40
|
+
gogetit create lxd01
|
41
|
+
gogetit create lxd01 --provider lxd
|
42
|
+
|
43
|
+
# For advanced network configuration
|
44
|
+
gogetit create lxd01 -p lxd -i 192.168.0.10
|
45
|
+
|
46
|
+
# When specifying multiple IPs, the first one will be chosen as gateway.
|
47
|
+
# And the IP which belongs to the gateway interface will be the IP of the FQDN
|
48
|
+
# of the container which is set by MAAS.
|
49
|
+
# The IPs should belong to networks defined and recognized by MAAS.
|
50
|
+
gogetit create lxd01 -p lxd -i 192.168.10.10 10.0.0.2
|
51
|
+
|
52
|
+
# When specifying multiple VLANs, the first one will be chosen as gateway.
|
53
|
+
# gogetit create lxd01 -p lxd -v 0 10 12
|
54
|
+
# gogetit create lxd01 -p lxd -v 10 11
|
55
|
+
|
56
|
+
gogetit create kvm01 -p libvirt
|
57
|
+
gogetit create kvm01 -p libvirt -i 192.168.10.10 10.0.0.2
|
58
|
+
|
59
|
+
# to provision with a bare metal machine
|
60
|
+
# gogetit create kvm01 -p bare
|
36
61
|
|
37
62
|
gogetit destroy lxd01
|
38
63
|
gogetit rebuild kvm01
|
39
64
|
|
40
65
|
# to create a container bootstrapping as a chef node
|
41
|
-
gogetit create --chef
|
66
|
+
gogetit create chef01 --chef
|
67
|
+
|
68
|
+
or
|
69
|
+
|
70
|
+
CHEF=true gogetit create chef01
|
71
|
+
|
42
72
|
|
43
73
|
# to destroy a container deleting corresponding chef node and client
|
44
|
-
gogetit destroy --chef
|
74
|
+
gogetit destroy chef01 --chef
|
45
75
|
```
|
46
76
|
|
47
77
|
```ruby
|
@@ -49,8 +79,9 @@ require 'gogetit'
|
|
49
79
|
```
|
50
80
|
|
51
81
|
## TODO
|
52
|
-
-
|
53
|
-
-
|
82
|
+
- Provisioning LXD with useful cloud-init user-data.
|
83
|
+
- Deploying bare metal machine from machine pool.
|
84
|
+
- Static IP auto-assignment with VLAN info.
|
54
85
|
|
55
86
|
## Development and Contributing
|
56
87
|
Clone and then execute followings:
|
data/lib/gogetit/cli.rb
CHANGED
@@ -17,16 +17,28 @@ module Gogetit
|
|
17
17
|
end
|
18
18
|
|
19
19
|
desc 'create (TYPE) NAME', 'Create either a container or KVM domain.'
|
20
|
-
method_option :
|
21
|
-
|
22
|
-
|
20
|
+
method_option :provider, :aliases => '-p', :type => :string, \
|
21
|
+
:default => 'lxd', :desc => 'A provider such as lxd and libvirt'
|
22
|
+
method_option :chef, :aliases => '-c', :type => :boolean, \
|
23
|
+
:default => false, :desc => 'Chef awareness'
|
24
|
+
|
25
|
+
method_option :vlans, :aliases => '-v', :type => :array, \
|
26
|
+
:desc => 'A list of VLAN IDs to connect to'
|
27
|
+
method_option :ipaddresses, :aliases => '-i', :type => :array, \
|
28
|
+
:desc => 'A list of static IPs to assign'
|
29
|
+
def create(name)
|
30
|
+
abort("vlans and ipaddresses can not be used at the same time.") \
|
31
|
+
if options['vlans'] and options['ipaddresses']
|
32
|
+
|
33
|
+
case options[:provider]
|
23
34
|
when 'lxd'
|
24
|
-
Gogetit.lxd.create(name)
|
35
|
+
Gogetit.lxd.create(name, options.to_hash)
|
25
36
|
when 'libvirt'
|
26
|
-
Gogetit.libvirt.create(name)
|
37
|
+
Gogetit.libvirt.create(name, options.to_hash)
|
27
38
|
else
|
28
39
|
abort('Invalid argument entered.')
|
29
40
|
end
|
41
|
+
|
30
42
|
# post-tasks
|
31
43
|
if options[:chef]
|
32
44
|
knife_bootstrap(name, type, Gogetit.config)
|
@@ -34,6 +46,7 @@ module Gogetit
|
|
34
46
|
end
|
35
47
|
Gogetit.config[:default][:user] ||= ENV['USER']
|
36
48
|
puts "ssh #{Gogetit.config[:default][:user]}@#{name}"
|
49
|
+
print "ssh #{Gogetit.config[:default][:user]}@#{name}"
|
37
50
|
end
|
38
51
|
|
39
52
|
desc 'destroy NAME', 'Destroy either a container or KVM domain.'
|
data/lib/gogetit/config.rb
CHANGED
@@ -61,23 +61,5 @@ module Gogetit
|
|
61
61
|
abort('Please define default configuration for GoGetIt at ~/.gogetit/gogetit.yml.')
|
62
62
|
end
|
63
63
|
config.merge!(symbolize_keys(YAML.load_file(conf_file)))
|
64
|
-
|
65
|
-
logger.debug('Define provider configuration directory..')
|
66
|
-
provider_conf_dir = user_gogetit_home + '/conf'
|
67
|
-
config[:provider_conf_dir] = provider_conf_dir
|
68
|
-
default_provider_conf_file = provider_conf_dir + '/default.yml'
|
69
|
-
config[:default_provider_conf_file] = default_provider_conf_file
|
70
|
-
if not File.exists?(default_provider_conf_file)
|
71
|
-
if not File.directory?(provider_conf_dir)
|
72
|
-
logger.debug('Creating provider configuration directory..')
|
73
|
-
FileUtils.mkdir(provider_conf_dir)
|
74
|
-
end
|
75
|
-
src = File.new(lib_dir + '/sample_conf/default.yml')
|
76
|
-
dst = Dir.new(provider_conf_dir)
|
77
|
-
logger.debug('Copying provider configuration file..')
|
78
|
-
FileUtils.cp(src, dst)
|
79
|
-
abort('Please define default configuration for providers at ~/.gogetit/conf/default.yml.')
|
80
|
-
end
|
81
|
-
|
82
64
|
end
|
83
65
|
end
|
data/lib/gogetit/maas.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'maas/client'
|
2
|
+
require 'ipaddr'
|
2
3
|
|
3
4
|
module Gogetit
|
4
5
|
class GogetMAAS
|
@@ -36,48 +37,142 @@ module Gogetit
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def domain_name_exists?(name)
|
40
|
+
logger.info("Calling <#{__method__.to_s}>")
|
39
41
|
return true if dnsresource_exists?(name) or machine_exists?(name)
|
40
42
|
end
|
41
43
|
|
44
|
+
def ip_reserved?(addresses)
|
45
|
+
logger.info("Calling <#{__method__.to_s}>")
|
46
|
+
ips = Set.new
|
47
|
+
addresses.each do |ip|
|
48
|
+
ips.add(IPAddr.new(ip))
|
49
|
+
end
|
50
|
+
|
51
|
+
reserved_ips = Set.new
|
52
|
+
conn.request(:get, ['ipaddresses']).each do |ip|
|
53
|
+
reserved_ips.add(IPAddr.new(ip['ip']))
|
54
|
+
end
|
55
|
+
|
56
|
+
rackcontroller_ips = Set.new
|
57
|
+
conn.request(:get, ['rackcontrollers']).each do |rctrl|
|
58
|
+
rctrl['ip_addresses'].each do |ip|
|
59
|
+
rackcontroller_ips.add(IPAddr.new(ip))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# reserved_ips | rackcontroller_ips
|
64
|
+
# Returns a new array by joining ary with other_ary,
|
65
|
+
# excluding any duplicates and preserving the order from the original array.
|
66
|
+
if ips.disjoint? reserved_ips | rackcontroller_ips
|
67
|
+
subnets = conn.request(:get, ['subnets'])
|
68
|
+
ipranges = conn.request(:get, ['ipranges'])
|
69
|
+
|
70
|
+
ifaces = []
|
71
|
+
|
72
|
+
ips.each do |ip|
|
73
|
+
available = false
|
74
|
+
subnets.each do |subnet|
|
75
|
+
if IPAddr.new(subnet['cidr']).include?(ip)
|
76
|
+
ipranges.each do |range|
|
77
|
+
if range['subnet']['id'] == subnet['id']
|
78
|
+
first = IPAddr.new(range['start_ip']).to_i
|
79
|
+
last = IPAddr.new(range['end_ip']).to_i
|
80
|
+
if (first..last) === ip.to_i
|
81
|
+
logger.info("#{ip} is available.")
|
82
|
+
available = true
|
83
|
+
subnets.delete(subnet)
|
84
|
+
subnet['ip'] = ip.to_s
|
85
|
+
ifaces << subnet
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
break if available
|
92
|
+
end
|
93
|
+
|
94
|
+
if not available
|
95
|
+
logger.info("#{ip.to_s} does not belong to any subnet pre-defined.")
|
96
|
+
return false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
else
|
101
|
+
logger.info("#{(ips & (reserved_ips | rackcontroller_ips)).to_a.join(', ')}\
|
102
|
+
is already reserved.")
|
103
|
+
return false
|
104
|
+
end
|
105
|
+
|
106
|
+
return ifaces
|
107
|
+
end
|
108
|
+
|
109
|
+
def ipaddresses_reserved?(ip)
|
110
|
+
logger.info("Calling <#{__method__.to_s}>")
|
111
|
+
conn.request(:get, ['ipaddresses']).each do |address|
|
112
|
+
if address['ip'] == ip
|
113
|
+
logger.info("#{ip} is reserved.")
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
|
42
120
|
def ipaddresses(op = nil, params = nil)
|
121
|
+
logger.info("Calling <#{__method__.to_s}>")
|
43
122
|
case op
|
44
123
|
when nil
|
45
124
|
conn.request(:get, ['ipaddresses'])
|
46
125
|
when 'reserve'
|
47
|
-
#
|
126
|
+
# params = {
|
48
127
|
# 'subnet' => '10.1.2.0/24',
|
49
128
|
# 'ip' => '10.1.2.8',
|
50
129
|
# 'hostname' => 'hostname',
|
51
130
|
# 'mac' => 'blahblah'
|
52
131
|
# }
|
53
132
|
default_param = { 'op' => op }
|
133
|
+
logger.info("#{params['ip']} is being reserved..")
|
54
134
|
conn.request(:post, ['ipaddresses'], default_param.merge!(params))
|
55
135
|
when 'release'
|
56
136
|
# Gogetit.maas.ipaddresses('release', {'ip' => '10.1.2.8'})
|
57
|
-
#
|
137
|
+
# params = {
|
58
138
|
# 'ip' => '10.1.2.8',
|
59
|
-
# 'hostname' => 'hostname',
|
60
|
-
# 'mac' => 'blahblah'
|
61
139
|
# }
|
62
140
|
default_param = { 'op' => op }
|
141
|
+
logger.info("#{params['ip']} is being released..")
|
63
142
|
conn.request(:post, ['ipaddresses'], default_param.merge!(params))
|
64
143
|
end
|
65
144
|
end
|
66
145
|
|
146
|
+
def interfaces(url = [], params = {})
|
147
|
+
logger.info("Calling <#{__method__.to_s}>")
|
148
|
+
|
149
|
+
url.insert(0, 'nodes')
|
150
|
+
url.insert(2, 'interfaces')
|
151
|
+
# ['nodes', system_id, 'interfaces']
|
152
|
+
|
153
|
+
case params['op']
|
154
|
+
when nil
|
155
|
+
conn.request(:get, url)
|
156
|
+
when 'create_vlan'
|
157
|
+
logger.info("Creating a vlan interface for id: #{params['vlan']}..")
|
158
|
+
conn.request(:post, url, params)
|
159
|
+
when 'link_subnet'
|
160
|
+
logger.info("Linking a subnet for id: #{url[3]}..")
|
161
|
+
conn.request(:post, url, params)
|
162
|
+
when 'unlink_subnet'
|
163
|
+
logger.info("Linking a subnet for id: #{url[3]}..")
|
164
|
+
conn.request(:post, url, params)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
67
168
|
def delete_dns_record(name)
|
68
169
|
logger.info("Calling <#{__method__.to_s}>")
|
69
|
-
id = nil
|
70
170
|
conn.request(:get, ['dnsresources']).each do |item|
|
71
171
|
if item['fqdn'] == name + '.' + get_domain
|
72
|
-
|
172
|
+
logger.info("#{item['fqdn']} is being deleted..")
|
173
|
+
conn.request(:delete, ['dnsresources', item['id']])
|
73
174
|
end
|
74
175
|
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
176
|
end
|
82
177
|
|
83
178
|
def refresh_pods
|
data/lib/gogetit/util.rb
CHANGED
@@ -91,7 +91,7 @@ module Gogetit
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def wait_until_available(fqdn, logger)
|
94
|
-
until ping_available?(fqdn)
|
94
|
+
until ping_available?(fqdn, logger)
|
95
95
|
logger.info("Calling <#{__method__.to_s}> for ping to be ready..")
|
96
96
|
sleep 3
|
97
97
|
end
|
@@ -104,8 +104,10 @@ module Gogetit
|
|
104
104
|
logger.info("#{fqdn} is now available to ssh..")
|
105
105
|
end
|
106
106
|
|
107
|
-
def ping_available?(
|
108
|
-
|
107
|
+
def ping_available?(host, logger)
|
108
|
+
# host can be both IP and FQDN.
|
109
|
+
logger.info("Calling <#{__method__.to_s}> for #{host}")
|
110
|
+
`ping -c 1 -W 1 #{host}`
|
109
111
|
$?.exitstatus == 0
|
110
112
|
end
|
111
113
|
|
@@ -116,5 +118,19 @@ module Gogetit
|
|
116
118
|
puts e
|
117
119
|
end
|
118
120
|
end
|
121
|
+
|
122
|
+
def check_ip_available(addresses, maas, logger)
|
123
|
+
logger.info("Calling <#{__method__.to_s}>")
|
124
|
+
# to do a ping test
|
125
|
+
addresses.each do |ip|
|
126
|
+
abort("#{ip} is already being used.") if ping_available?(ip, logger)
|
127
|
+
end
|
128
|
+
# to check with MAAS
|
129
|
+
ifaces = maas.ip_reserved?(addresses)
|
130
|
+
abort("one of #{addresses.join(', ')} is already being used.") \
|
131
|
+
unless ifaces
|
132
|
+
return ifaces
|
133
|
+
end
|
134
|
+
|
119
135
|
end
|
120
136
|
end
|
data/lib/gogetit/version.rb
CHANGED
data/lib/providers/libvirt.rb
CHANGED
@@ -42,16 +42,70 @@ module Gogetit
|
|
42
42
|
.value
|
43
43
|
end
|
44
44
|
|
45
|
+
def generate_nics(ifaces, domain)
|
46
|
+
abort("There is no dns server specified for the gateway network.") \
|
47
|
+
unless ifaces[0]['dns_servers'][0]
|
48
|
+
abort("There is no gateway specified for the gateway network.") \
|
49
|
+
unless ifaces[0]['gateway_ip']
|
50
|
+
|
51
|
+
# It seems the first IP has to belong to the untagged VLAN in the Fabric.
|
52
|
+
abort("The first IP you entered does not belong to the untagged VLAN in the Fabric.") \
|
53
|
+
unless ifaces[0]['vlan']['name'] == 'untagged'
|
54
|
+
|
55
|
+
domain[:ifaces] = ifaces
|
56
|
+
domain[:nic] = []
|
57
|
+
|
58
|
+
ifaces.each_with_index do |iface,index|
|
59
|
+
if index == 0
|
60
|
+
if iface['vlan']['name'] == 'untagged'
|
61
|
+
nic = {
|
62
|
+
network: config[:default][:native_bridge],
|
63
|
+
portgroup: config[:default][:native_bridge]
|
64
|
+
}
|
65
|
+
elsif iface['vlan']['name'] != 'untagged'
|
66
|
+
nic = {
|
67
|
+
network: config[:default][:native_bridge],
|
68
|
+
portgroup: config[:default][:native_bridge] + '-' + iface['vlan']['vid'].to_s
|
69
|
+
}
|
70
|
+
end
|
71
|
+
domain[:nic].push(nic)
|
72
|
+
elsif index > 0
|
73
|
+
# Only if the fisrt interface has untagged VLAN,
|
74
|
+
# it will be configured with VLANs.
|
75
|
+
# This will not be hit as of now and might be deprecated.
|
76
|
+
if ifaces[0]['vlan']['name'] != 'untagged'
|
77
|
+
nic = {
|
78
|
+
network: config[:default][:native_bridge],
|
79
|
+
portgroup: config[:default][:native_bridge] + '-' + iface['vlan']['vid'].to_s
|
80
|
+
}
|
81
|
+
domain[:nic].push(nic)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return domain
|
86
|
+
end
|
87
|
+
|
45
88
|
# subject.create(name: 'test01')
|
46
|
-
def create(name,
|
89
|
+
def create(name, options = nil)
|
47
90
|
logger.info("Calling <#{__method__.to_s}>")
|
48
|
-
|
49
|
-
|
50
|
-
|
91
|
+
abort("Domain #{name} already exists! Please check both on MAAS and libvirt.") \
|
92
|
+
if maas.domain_name_exists?(name) or domain_exists?(name)
|
93
|
+
|
94
|
+
domain = config[:libvirt][:specs][:default]
|
95
|
+
if options['ipaddresses']
|
96
|
+
ifaces = check_ip_available(options['ipaddresses'], maas, logger)
|
97
|
+
domain = generate_nics(ifaces, domain)
|
98
|
+
elsif options[:vlans]
|
99
|
+
#check_vlan_available(options[:vlans])
|
100
|
+
else
|
101
|
+
domain[:nic] = [
|
102
|
+
{
|
103
|
+
network: config[:default][:native_bridge],
|
104
|
+
portgroup: config[:default][:native_bridge]
|
105
|
+
}
|
106
|
+
]
|
51
107
|
end
|
52
108
|
|
53
|
-
conf_file ||= config[:default_provider_conf_file]
|
54
|
-
domain = symbolize_keys(YAML.load_file(conf_file))
|
55
109
|
domain[:name] = name
|
56
110
|
domain[:uuid] = SecureRandom.uuid
|
57
111
|
|
@@ -60,6 +114,65 @@ module Gogetit
|
|
60
114
|
|
61
115
|
system_id = maas.get_system_id(domain[:name])
|
62
116
|
maas.wait_until_state(system_id, 'Ready')
|
117
|
+
|
118
|
+
# To configure interfaces
|
119
|
+
if options['ipaddresses']
|
120
|
+
|
121
|
+
# It assumes you only have a physical interfaces.
|
122
|
+
interfaces = maas.interfaces([system_id])
|
123
|
+
maas.interfaces(
|
124
|
+
[system_id, interfaces[0]['id']],
|
125
|
+
{
|
126
|
+
'op' => 'unlink_subnet',
|
127
|
+
'id' => interfaces[0]['links'][0]['id']
|
128
|
+
}
|
129
|
+
)
|
130
|
+
|
131
|
+
maas.interfaces(
|
132
|
+
[system_id, interfaces[0]['id']],
|
133
|
+
{
|
134
|
+
'op' => 'link_subnet',
|
135
|
+
'mode' => 'STATIC',
|
136
|
+
'subnet' => ifaces[0]['id'],
|
137
|
+
'ip_address' => ifaces[0]['ip'],
|
138
|
+
'default_gateway' => 'True',
|
139
|
+
'force' => 'False'
|
140
|
+
}
|
141
|
+
)
|
142
|
+
|
143
|
+
if domain[:ifaces].length > 1
|
144
|
+
ifaces.shift
|
145
|
+
|
146
|
+
# VLAN configuration
|
147
|
+
ifaces.each_with_index do |iface,index|
|
148
|
+
params = {
|
149
|
+
'op' => 'create_vlan',
|
150
|
+
'vlan' => iface['vlan']['id'],
|
151
|
+
'parent' => interfaces[0]['id']
|
152
|
+
}
|
153
|
+
maas.interfaces([system_id], params)
|
154
|
+
|
155
|
+
interfaces = maas.interfaces([system_id])
|
156
|
+
interfaces.shift
|
157
|
+
|
158
|
+
maas.interfaces([system_id, interfaces[index]['id']],
|
159
|
+
{
|
160
|
+
'op' => 'link_subnet',
|
161
|
+
'mode' => 'STATIC',
|
162
|
+
'subnet' => ifaces[index]['id'],
|
163
|
+
'ip_address' => ifaces[index]['ip'],
|
164
|
+
'default_gateway' => 'False',
|
165
|
+
'force' => 'False'
|
166
|
+
}
|
167
|
+
)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
elsif options[:vlans]
|
172
|
+
#check_vlan_available(options[:vlans])
|
173
|
+
else
|
174
|
+
end
|
175
|
+
|
63
176
|
logger.info("Calling to deploy...")
|
64
177
|
maas.conn.request(:post, ['machines', system_id], {'op' => 'deploy'})
|
65
178
|
maas.wait_until_state(system_id, 'Deployed')
|
@@ -123,10 +236,10 @@ module Gogetit
|
|
123
236
|
doc = define_volumes(doc, domain)
|
124
237
|
doc = add_nic(doc, domain[:nic])
|
125
238
|
|
126
|
-
#print_xml(doc)
|
127
|
-
#volumes.each do |v|
|
128
|
-
#
|
129
|
-
#end
|
239
|
+
# print_xml(doc)
|
240
|
+
# volumes.each do |v|
|
241
|
+
# print_xml(v)
|
242
|
+
# end
|
130
243
|
|
131
244
|
return Oga::XML::Generator.new(doc).to_xml
|
132
245
|
end
|
data/lib/providers/lxd.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'hyperkit'
|
2
2
|
require 'gogetit/util'
|
3
|
+
require 'yaml'
|
3
4
|
|
4
5
|
module Gogetit
|
5
6
|
class GogetLXD
|
@@ -41,17 +42,179 @@ module Gogetit
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
def
|
45
|
+
def generate_args(options)
|
46
|
+
args = {}
|
47
|
+
args[:devices] = {}
|
48
|
+
|
49
|
+
ifaces = check_ip_available(options['ipaddresses'], maas, logger)
|
50
|
+
abort("There is no dns server specified for the gateway network.") \
|
51
|
+
unless ifaces[0]['dns_servers'][0]
|
52
|
+
abort("There is no gateway specified for the gateway network.") \
|
53
|
+
unless ifaces[0]['gateway_ip']
|
54
|
+
args[:ifaces] = ifaces
|
55
|
+
args[:config] = {
|
56
|
+
'user.network-config': {
|
57
|
+
'version' => 1,
|
58
|
+
'config' => [
|
59
|
+
{
|
60
|
+
'type' => 'nameserver',
|
61
|
+
'address' => ifaces[0]['dns_servers'][0]
|
62
|
+
}
|
63
|
+
]
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
ifaces.each_with_index do |iface,index|
|
68
|
+
if index == 0
|
69
|
+
iface_conf = {
|
70
|
+
'type' => 'physical',
|
71
|
+
'name' => "eth#{index}",
|
72
|
+
'subnets' => [
|
73
|
+
{
|
74
|
+
'type' => 'static',
|
75
|
+
'ipv4' => true,
|
76
|
+
'address' => iface['ip'] + '/' + iface['cidr'].split('/')[1],
|
77
|
+
'gateway' => iface['gateway_ip'],
|
78
|
+
'mtu' => iface['vlan']['mtu'],
|
79
|
+
'control' => 'auto'
|
80
|
+
}
|
81
|
+
]
|
82
|
+
}
|
83
|
+
elsif index > 0
|
84
|
+
if ifaces[0]['vlan']['name'] != 'untagged'
|
85
|
+
iface_conf = {
|
86
|
+
'type' => 'physical',
|
87
|
+
'name' => "eth#{index}",
|
88
|
+
'subnets' => [
|
89
|
+
{
|
90
|
+
'type' => 'static',
|
91
|
+
'ipv4' => true,
|
92
|
+
'address' => iface['ip'] + '/' + iface['cidr'].split('/')[1],
|
93
|
+
'mtu' => iface['vlan']['mtu'],
|
94
|
+
'control' => 'auto'
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
elsif ifaces[0]['vlan']['name'] == 'untagged'
|
99
|
+
iface_conf = {
|
100
|
+
'type' => 'vlan',
|
101
|
+
'name' => "eth0.#{iface['vlan']['vid'].to_s}",
|
102
|
+
'vlan_id' => iface['vlan']['vid'].to_s,
|
103
|
+
'vlan_link' => 'eth0',
|
104
|
+
'subnets' => [
|
105
|
+
{
|
106
|
+
'type' => 'static',
|
107
|
+
'ipv4' => true,
|
108
|
+
'address' => iface['ip'] + '/' + iface['cidr'].split('/')[1],
|
109
|
+
'mtu' => iface['vlan']['mtu'],
|
110
|
+
'control' => 'auto'
|
111
|
+
}
|
112
|
+
]
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
args[:config][:'user.network-config']['config'].push(iface_conf)
|
118
|
+
end
|
119
|
+
|
120
|
+
args[:config][:"user.network-config"] = \
|
121
|
+
YAML.dump(args[:config][:"user.network-config"])[4..-1]
|
122
|
+
|
123
|
+
# To configure devices
|
124
|
+
ifaces.each_with_index do |iface,index|
|
125
|
+
if index == 0
|
126
|
+
if iface['vlan']['name'] == 'untagged' # or vid == 0
|
127
|
+
args[:devices][:"eth#{index}"] = {
|
128
|
+
mtu: iface['vlan']['mtu'].to_s, #This must be string
|
129
|
+
name: "eth#{index}",
|
130
|
+
nictype: 'bridged',
|
131
|
+
parent: config[:default][:native_bridge],
|
132
|
+
type: 'nic'
|
133
|
+
}
|
134
|
+
elsif iface['vlan']['name'] != 'untagged' # or vid != 0
|
135
|
+
args[:devices][:"eth#{index}"] = {
|
136
|
+
mtu: iface['vlan']['mtu'].to_s, #This must be string
|
137
|
+
name: "eth#{index}",
|
138
|
+
nictype: 'bridged',
|
139
|
+
parent: config[:default][:native_bridge] + "-" + iface['vlan']['vid'].to_s,
|
140
|
+
type: 'nic'
|
141
|
+
}
|
142
|
+
end
|
143
|
+
# When ifaces[0]['vlan']['name'] == 'untagged' and index > 0,
|
144
|
+
# it does not need to generate more devices
|
145
|
+
# since it will configure the IPs with tagged VLANs.
|
146
|
+
elsif ifaces[0]['vlan']['name'] != 'untagged'
|
147
|
+
args[:devices][:"eth#{index}"] = {
|
148
|
+
mtu: iface['vlan']['mtu'].to_s, #This must be string
|
149
|
+
name: "eth#{index}",
|
150
|
+
nictype: 'bridged',
|
151
|
+
parent: config[:default][:native_bridge] + "-" + iface['vlan']['vid'].to_s,
|
152
|
+
type: 'nic'
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
return args
|
158
|
+
end
|
159
|
+
|
160
|
+
def create(name, options = {})
|
45
161
|
logger.info("Calling <#{__method__.to_s}>")
|
46
|
-
|
47
|
-
|
48
|
-
|
162
|
+
abort("Container or Hostname #{name} already exists!") \
|
163
|
+
if container_exists?(name) or maas.domain_name_exists?(name)
|
164
|
+
|
165
|
+
args = {}
|
166
|
+
if options['ipaddresses']
|
167
|
+
args = generate_args(options)
|
168
|
+
elsif options[:vlans]
|
169
|
+
#check_vlan_available(options[:vlans])
|
170
|
+
else
|
171
|
+
args[:profiles] ||= config[:lxd][:profiles]
|
49
172
|
end
|
50
173
|
|
51
174
|
args[:alias] ||= config[:lxd][:default_alias]
|
52
|
-
args[:profiles] ||= config[:lxd][:profiles]
|
53
175
|
args[:sync] ||= true
|
176
|
+
|
54
177
|
conn.create_container(name, args)
|
178
|
+
container = conn.container(name)
|
179
|
+
|
180
|
+
if options['vlans'] or options['ipaddresses']
|
181
|
+
container.devices = args[:devices].merge!(container.devices.to_hash)
|
182
|
+
conn.update_container(name, container)
|
183
|
+
# Fetch container object again
|
184
|
+
container = conn.container(name)
|
185
|
+
|
186
|
+
# Generate params to reserve IPs
|
187
|
+
args[:ifaces].each_with_index do |iface,index|
|
188
|
+
if index == 0
|
189
|
+
params = {
|
190
|
+
'subnet' => iface['cidr'],
|
191
|
+
'ip' => iface['ip'],
|
192
|
+
'hostname' => name,
|
193
|
+
'mac' => container[:expanded_config][:"volatile.eth#{index}.hwaddr"]
|
194
|
+
}
|
195
|
+
elsif index > 0
|
196
|
+
# if dot, '.', is used as a conjunction instead of '-', it fails ocuring '404 not found'.
|
197
|
+
# if under score, '_', is used as a conjunction instead of '-', it breaks MAAS DNS somehow..
|
198
|
+
if args[:ifaces][0]['vlan']['name'] == 'untagged'
|
199
|
+
params = {
|
200
|
+
'subnet' => iface['cidr'],
|
201
|
+
'ip' => iface['ip'],
|
202
|
+
'hostname' => 'eth0' + '-' + iface['vlan']['vid'].to_s + '-' + name,
|
203
|
+
'mac' => container[:expanded_config][:"volatile.eth0.hwaddr"]
|
204
|
+
}
|
205
|
+
elsif args[:ifaces][0]['vlan']['name'] != 'untagged'
|
206
|
+
params = {
|
207
|
+
'subnet' => iface['cidr'],
|
208
|
+
'ip' => iface['ip'],
|
209
|
+
'hostname' => "eth#{index}" + '-' + name,
|
210
|
+
'mac' => container[:expanded_config][:"volatile.eth#{index}.hwaddr"]
|
211
|
+
}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
maas.ipaddresses('reserve', params)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
55
218
|
conn.start_container(name, :sync=>"true")
|
56
219
|
|
57
220
|
fqdn = name + '.' + maas.get_domain
|
@@ -62,12 +225,39 @@ module Gogetit
|
|
62
225
|
|
63
226
|
def destroy(name, args = {})
|
64
227
|
logger.info("Calling <#{__method__.to_s}>")
|
228
|
+
|
229
|
+
container = conn.container(name)
|
65
230
|
args[:sync] ||= true
|
231
|
+
|
66
232
|
if get_state(name) == 'Running'
|
67
233
|
conn.stop_container(name, args)
|
68
234
|
end
|
235
|
+
|
69
236
|
wait_until_state(name, 'Stopped')
|
70
237
|
conn.delete_container(name, args)
|
238
|
+
|
239
|
+
if container[:config][:"user.network-config"]
|
240
|
+
net_conf = YAML.load(
|
241
|
+
container[:config][:"user.network-config"]
|
242
|
+
)['config']
|
243
|
+
# To remove DNS configuration
|
244
|
+
net_conf.shift
|
245
|
+
|
246
|
+
net_conf.each do |nic|
|
247
|
+
if nic['subnets'][0]['type'] == 'static'
|
248
|
+
# It assumes we only assign a single subnet on a VLAN.
|
249
|
+
# Subnets in a VLAN, VLANs in a Fabric
|
250
|
+
ip = nic['subnets'][0]['address'].split('/')[0]
|
251
|
+
|
252
|
+
if maas.ipaddresses_reserved?(ip)
|
253
|
+
maas.ipaddresses('release', { 'ip' => ip })
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# When multiple static IPs were reserved, it will not delete anything
|
260
|
+
# since they are deleted when releasing the IPs above.
|
71
261
|
maas.delete_dns_record(name)
|
72
262
|
logger.info("#{name} has been destroyed.")
|
73
263
|
true
|
data/lib/sample_conf/gogetit.yml
CHANGED
@@ -1,18 +1,45 @@
|
|
1
1
|
default:
|
2
2
|
user: ubuntu
|
3
|
-
|
4
|
-
|
3
|
+
native_bridge: my_favorite_bridge
|
4
|
+
# etcd:
|
5
|
+
# url: http://etcd.example.com:2379
|
6
|
+
# about MAAS
|
5
7
|
maas:
|
6
8
|
key: K:E:Y
|
7
9
|
url: http://maas.example.com/MAAS/api/2.0
|
10
|
+
# about LXD
|
8
11
|
lxd:
|
9
12
|
url: https://lxd.example.com:8443
|
10
13
|
name: REMOTE_NAME
|
11
14
|
default_alias: ubuntu-16.04
|
12
15
|
profiles:
|
13
16
|
- my_favorite_bridge
|
17
|
+
# about Libvirt
|
14
18
|
libvirt:
|
15
19
|
url: qemu+ssh://WHOAMI@kvm.example.com/system
|
20
|
+
specs:
|
21
|
+
default:
|
22
|
+
vcpu: 1
|
23
|
+
memory: 1
|
24
|
+
disk:
|
25
|
+
root:
|
26
|
+
pool: ssd
|
27
|
+
capacity: 4
|
28
|
+
ceph:
|
29
|
+
vcpu: 4
|
30
|
+
memory: 8
|
31
|
+
disk:
|
32
|
+
root:
|
33
|
+
pool: ssd
|
34
|
+
capacity: 4
|
35
|
+
data:
|
36
|
+
-
|
37
|
+
pool: hdd
|
38
|
+
capacity: 8
|
39
|
+
-
|
40
|
+
pool: hdd
|
41
|
+
capacity: 8
|
42
|
+
# about Chef
|
16
43
|
chef:
|
17
44
|
chef_repo_root: /SOMEWHERE/CHEF_REPO
|
18
45
|
bootstrap:
|
@@ -0,0 +1,17 @@
|
|
1
|
+
config:
|
2
|
+
# raw.lxc: lxc.aa_profile=unconfined
|
3
|
+
# security.privileged: "true"
|
4
|
+
user.user-data: |
|
5
|
+
#cloud-config
|
6
|
+
ssh_authorized_keys:
|
7
|
+
- ssh-rsa blahblah someone@somewhere
|
8
|
+
apt_mirror: http://repo.example.com/ubuntu/
|
9
|
+
description: ""
|
10
|
+
devices:
|
11
|
+
eth0:
|
12
|
+
mtu: "8954"
|
13
|
+
name: eth0
|
14
|
+
nictype: bridged
|
15
|
+
parent: somebr
|
16
|
+
type: nic
|
17
|
+
name: somebr
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gogetit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Don Draper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -215,9 +215,8 @@ files:
|
|
215
215
|
- lib/gogetit/version.rb
|
216
216
|
- lib/providers/libvirt.rb
|
217
217
|
- lib/providers/lxd.rb
|
218
|
-
- lib/sample_conf/ceph.yml
|
219
|
-
- lib/sample_conf/default.yml
|
220
218
|
- lib/sample_conf/gogetit.yml
|
219
|
+
- lib/sample_conf/lxd_profile.yaml
|
221
220
|
- lib/template/disk.xml
|
222
221
|
- lib/template/domain.xml
|
223
222
|
- lib/template/nic.xml
|
data/lib/sample_conf/ceph.yml
DELETED