pec 0.1.2 → 0.1.3

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: a23835f633410abdb1ca9c614c3d8e386101c17d
4
- data.tar.gz: 7f3df00c0a75012a93898c33bc9b5f28eb213fa5
3
+ metadata.gz: 409aebdf8468165b18f5bf9e130a64c0e8d52e89
4
+ data.tar.gz: 79635469bbd61536714e08715cfafe4a9c5ab77e
5
5
  SHA512:
6
- metadata.gz: 2f05e20813ffd29318f0ae79608907412682b6aaa52371b7dce7d648351c470f4aba4aeea632020d5dc8b7ab85f6a7c02d2efff21619a33810c13703e6576557
7
- data.tar.gz: 24b4f770903b98f04c28896c749d8fc784f4896ef2eb13c45cab6db9cc402ee8073d623e97492279dd0a7ce0579d5f1e99746a17f1b0e3cbe803d93d7a6390f7
6
+ metadata.gz: 683b49175521024e1a3d617678efd3fdc4896a3905c1d25827c6fb0eb2b2be055ba6d971b1468b0572cb0d0f7c0437ee953ff2bec9ef51470ba875b61143c0da
7
+ data.tar.gz: 58be8b103ec3b6353d0eba896d31b1c4738dec33a6fc8f0b85f3710c5f3f70a63fd04b942306f9435901ad9349194fbf4b6bfaedbeba025f84dae8abe2ed1380
@@ -0,0 +1 @@
1
+ service_name: travis-ci
@@ -1,3 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
3
+ - 2.0.0
4
+ - 2.1.6
5
+ - 2.2.2
data/Gemfile CHANGED
@@ -12,3 +12,6 @@ group :development, :test do
12
12
  gem "rspec"
13
13
  gem "rake"
14
14
  end
15
+ group :test do
16
+ gem 'coveralls', :require => false
17
+ end
data/README.md CHANGED
@@ -1,25 +1,33 @@
1
1
  # Pec
2
-
2
+ [![Build Status](https://travis-ci.org/pyama86/pec.svg?branch=master)](https://travis-ci.org/pyama86/pec)
3
3
  OpenStackにおいて複数サーバの起動や、
4
4
  DHCPサーバがない状況でのIP自動採番を実現します。
5
5
 
6
- ## インストール方法
6
+ ## Install
7
7
 
8
8
 
9
9
  $ gem install pec
10
10
 
11
- ## 使用方法
12
- ### コマンド
13
- 実行ディレクトリに存在するPec.yamlに基づきホストを作成します。
14
- ホスト名が指定された場合はそのホストのみ作成します。
11
+ ## Usage
12
+
13
+ 定義ファイル作成
14
+
15
+ $ pec init
16
+
17
+ ```
18
+ create - /Pec.yaml
19
+ create - /user_datas/web_server.yaml.sample
20
+ ```
21
+
22
+ Pec.yamlに基づきホストを作成します。
23
+ ホスト名が指定された場合はそのホストのみ作成、削除します。
15
24
 
16
25
  $ pec up <hostname>
17
26
 
18
27
  $ pec destroy <hostname>
19
28
 
20
- ### 設定ファイル
21
- [fog](https://github.com/fog/fog)を利用しているので、fogの設定を行ってください。
22
-
29
+ ### Configure
30
+ #### ~/.fog
23
31
  ```
24
32
  % cat ~/.fog
25
33
  default:
@@ -30,7 +38,7 @@ default:
30
38
  ```
31
39
 
32
40
 
33
- `実行ディレクトリ/Pec.yaml`にVMの設定を実施します。
41
+ #### Pec.yaml
34
42
  ```
35
43
  pyama-test001:
36
44
  image: centos-7.1_chef-12.3_puppet-3.7
@@ -43,10 +51,13 @@ pyama-test001:
43
51
  dns1: 8.8.8.8
44
52
  dns2: 8.8.8.8
45
53
  eth1:
46
- bootproto: static
47
- ip_address: 20.2.2.0/24
48
- dns1: 8.8.8.8
49
- dns2: 8.8.8.8
54
+ bootproto: dhcp
55
+ security_group:
56
+ - default
57
+ - ssh
58
+ templates:
59
+ - base.yaml
60
+ - webserver.yaml
50
61
  user_data:
51
62
  hostname: pyama-test001
52
63
  fqdn: pyama-test001.ikemen.com
@@ -54,28 +65,30 @@ pyama-test001:
54
65
  pyama-test002:
55
66
  image: centos-7.1_chef-12.3_puppet-3.7
56
67
  flavor: m1.midium
57
- networks:
58
- eth0:
59
- bootproto: static
60
- ip_address: 10.0.0.64/26
61
- gateway: 10.0.0.127
62
- dns1: 8.8.8.8
63
- dns2: 8.8.8.8
64
- path: /etc/sysconfig/network-scripts/ifcfg-bond0
65
- eth1:
66
- bootproto: dhcp
67
- user_data:
68
- hostname: pyama-test002
69
- fqdn: pyama-test002.ikemen.com
70
- repo_releasever: 7.1.1503
68
+ ・・・
69
+
71
70
  ```
72
- `VM名`,`image`,`flavor`は必須項目です。
73
- `networks`について指定する場合は、`bootproto`,`ip_address`が必須です。`ip_address`は`xxx.xxx.xxx.xxx/yy`の方式を想定しており、ネットワークアドレスが指定された場合、そのサブネットで未使用のアドレスを自動で採番します。
74
- またnetworksが指定された場合は自動でVM内に`/etc/sysconfig/network-script/ifcfg-ethXXX`を作成します。
75
- ファイルの保存パスに指定が必要な場合は`path`を指定してください。
76
- `bootproto`,`ip_address`,`path`以外の値が設定されている場合は、`ifcfg-ethXXX`にそのままの値が`KEY=value`形式で出力されます。
77
- `user_data`については`nova api`にネットワーク設定を加えて引き渡すため、cloud-init記法に準拠します。
71
+ ##### Detail
72
+
73
+ | 項目名 | 説明 | 必須 | 例示 |
74
+ | -------------- | ---------------------------------------------- | ---- | ------------------------------- |
75
+ | instance_name| インスタンス名 | ○ | pyama-test001 |
76
+ | image | イメージ名 | ○ | centos-7.1_chef-12.3_puppet-3.7 |
77
+ | flavor | フレーバー名 | ○ | m1.small |
78
+ | networks | ネットワーク定義 | - | [] |
79
+ | security_group | セキュリティグループ名 | - | [default,ssh] |
80
+ | templates | `user_data`のテンプレート.`./user_datas`に配置 | - | [base.yaml,webserver.yaml] |
81
+ | user_data | cloud-init記法に準拠 | - | - |
78
82
 
83
+ ##### Networks
84
+ | 項目名 | 説明 | 必須 | 例示 |
85
+ | ------------ | ---------------- | ---- | -------------- |
86
+ | device_name | デバイス名 | ○ | eth0 |
87
+ | bootproto | 設定方式 | ○ | static or dhcp |
88
+ | ip_address | IPアドレス(CIDR) | ※ | 10.1.1.1/24 |
89
+ | path | NW設定保存パス | | default:/etc/sysconfig/network-scripts/ifcfg-[device_name] |
90
+ ※ bootproto=staticの場合必須
91
+ 上記以外の項目は設定ファイルに`KEY=value`形式で出力されます。
79
92
 
80
93
  ## Author
81
94
  * pyama86
data/Rakefile CHANGED
@@ -1 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new("spec")
5
+ task :default => :spec
data/lib/pec.rb CHANGED
@@ -2,6 +2,7 @@ require 'fog'
2
2
  require 'ip'
3
3
  require "pec/version"
4
4
  require "pec/query"
5
+ require "pec/errors"
5
6
  require "pec/vm_director"
6
7
  require "pec/configure"
7
8
  require "pec/configure/sample"
@@ -6,12 +6,16 @@ module Pec
6
6
  desc 'init', 'create sample config'
7
7
  def init
8
8
  dirname = "user_datas"
9
- FileUtils.mkdir_p(dirname) unless FileTest.exist?(dirname)
10
-
11
- open("Pec.yaml","w") do |e|
12
- YAML.dump(Pec::Configure::Sample.pec_file, e)
13
- end unless File.exist?("Pec.yaml")
14
-
9
+ unless FileTest.exist?(dirname)
10
+ FileUtils.mkdir_p(dirname)
11
+ puts "create directry user_datas"
12
+ end
13
+ unless File.exist?("Pec.yaml")
14
+ open("Pec.yaml","w") do |e|
15
+ YAML.dump(Pec::Configure::Sample.pec_file, e)
16
+ end
17
+ puts "create configure file Pec.yaml"
18
+ end
15
19
  open("#{dirname}/web_server.yaml.sample","w") do |e|
16
20
  YAML.dump(Pec::Configure::Sample.user_data, e)
17
21
  end if FileTest.exist?(dirname)
@@ -24,21 +28,41 @@ module Pec
24
28
  config.load("Pec.yaml")
25
29
 
26
30
  director = Pec::VmDirector.new
31
+
27
32
  config.each do |host|
28
33
  next if !host_name.nil? && host.name != host_name
29
- puts "can't create server:#{host.name}" unless director.make(host)
34
+
35
+ begin
36
+ director.make(host)
37
+ rescue Pec::Errors::Error => e
38
+ puts e
39
+ puts "can't create server:#{host.name}"
40
+ rescue Excon::Errors::Error => e
41
+ JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
42
+ end
30
43
  end if config
44
+ rescue Errno::ENOENT => e
45
+ puts e
46
+ rescue Pec::Errors::Configure => e
47
+ puts "configure can't load"
31
48
  end
49
+
32
50
  option :force , type: :boolean, aliases: "-f"
33
51
  desc "destroy", "delete vm"
34
52
  def destroy(name = nil)
35
53
  config = Pec::Configure.new
36
54
  config.load("Pec.yaml")
37
-
38
55
  config.each do |host|
39
56
  next if !name.nil? && host.name != name
40
- Pec::Compute::Server.new.destroy!(host.name) if yes?("#{host.name}: Are you sure you want to destroy the '#{host.name}' VM? [y/N]") || options["force"]
57
+ begin
58
+ Pec::Compute::Server.new.destroy!(host.name) if options[:force] || yes?("#{host.name}: Are you sure you want to destroy the '#{host.name}' VM? [y/N]")
59
+ rescue Pec::Errors::Error => e
60
+ puts e
61
+ puts "can't create server:#{host.name}"
62
+ end
41
63
  end if config
64
+ rescue Errno::ENOENT => e
65
+ puts e
42
66
  end
43
67
  end
44
68
  end
@@ -6,9 +6,6 @@ module Pec
6
6
  security_groups.each do |sg_name|
7
7
  response = Fog::Compute[:openstack].add_security_group(server_id, sg_name)
8
8
  end if security_groups
9
- rescue Excon::Errors::Error => e
10
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
11
- false
12
9
  end
13
10
  end
14
11
  end
@@ -5,10 +5,7 @@ module Pec
5
5
  include Query
6
6
  def create(name, image_ref, flavor_ref, ports, options)
7
7
  networks = ports.map do |port|
8
- if port.used?
9
- puts "port-id:#{port.id} ip-addr:#{port.ip_address} in used"
10
- return false
11
- end
8
+ raise(Pec::Errors::Port, "port-id:#{port.id} ip-addr:#{port.ip_address} in used") if port.used?
12
9
  puts "#{name}: assingn ip #{port.ip_address}"
13
10
  { port_id: port.id }
14
11
  end if ports
@@ -22,10 +19,6 @@ module Pec
22
19
  end
23
20
 
24
21
  response.data[:body]["server"]["id"]
25
-
26
- rescue Excon::Errors::Error => e
27
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
28
- false
29
22
  end
30
23
 
31
24
  def exists?(server_name)
@@ -34,20 +27,12 @@ module Pec
34
27
 
35
28
  def destroy!(server_name)
36
29
  server = fetch(server_name)
37
- unless server
38
- puts "server_name:#{server_name} is not fond!"
39
- return
40
- end
41
-
30
+ raise(Pec::Errors::Host, "server_name:#{server_name} is not fond!") unless server
42
31
  response = Fog::Compute[:openstack].delete_server(server["id"]) if server
43
32
 
44
33
  if response && response[:status] == 204
45
34
  puts "server_name:#{server_name} is deleted!"
46
35
  end
47
-
48
- rescue Excon::Errors::Error => e
49
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
50
- false
51
36
  end
52
37
  end
53
38
  end
@@ -9,8 +9,8 @@ module Pec
9
9
  @configure ||= []
10
10
  @configure << host if host
11
11
  end
12
- rescue Psych::SyntaxError => e
13
- puts e
12
+ rescue Psych::SyntaxError,NoMethodError => e
13
+ raise(Pec::Errors::Configure, e)
14
14
  end
15
15
 
16
16
  def each
@@ -18,23 +18,19 @@ module Pec
18
18
 
19
19
  def check_require_key(name, config)
20
20
  err = %w(bootproto).find {|k| !config[1].key?(k)}
21
- return true if err.nil?
22
- puts "skip! #{name}: #{err} is required!"
23
- false
21
+ raise(Pec::Errors::Ethernet, "skip! #{name}: #{err} is required!") unless err.nil?
22
+ true
24
23
  end
25
24
 
26
25
  def check_network_key(name, config)
27
26
  net = config[1]
28
27
  case
29
28
  when (net["bootproto"] == "static" && net["ip_address"].nil?)
30
- puts "skip! #{name}: ip_address is required by bootproto static"
31
- return false
29
+ raise(Pec::Errors::Ethernet, "skip! #{name}: ip_address is required by bootproto static")
32
30
  when (!net["bootproto"] == "static" && !net["bootproto"] == "dhcp")
33
- puts "skip! #{name}: bootproto set the value dhcp or static"
34
- return false
31
+ raise(Pec::Errors::Ethernet, "skip! #{name}: bootproto set the value dhcp or static")
35
32
  when (!net["bootproto"] == "static" && !net["bootproto"] == "dhcp")
36
- puts "skip! #{name}: bootproto set the value dhcp or static"
37
- return false
33
+ raise(Pec::Errors::Ethernet, "skip! #{name}: bootproto set the value dhcp or static")
38
34
  end
39
35
  true
40
36
  end
@@ -28,9 +28,8 @@ module Pec
28
28
 
29
29
  def check_require_key(config)
30
30
  err = %w(image flavor).find {|r| !config[1].key?(r)}
31
- return true if err.nil?
32
- puts "skip! #{config[0]}: #{err} is required!"
33
- false
31
+ raise(Pec::Errors::Host,"skip! #{config[0]}: #{err} is required!") unless err.nil?
32
+ true
34
33
  end
35
34
  end
36
35
  end
@@ -12,43 +12,36 @@ module Pec
12
12
  end
13
13
 
14
14
  def get_template(config)
15
- merge_template = {}
16
- config.templates.each do |template|
17
- if FileTest.exist?("user_datas/#{template}")
18
- merge_template.merge!(YAML.load_file("user_datas/#{template}").to_hash)
19
- else
20
- puts "template:#{temlate} is not fond!"
21
- end
15
+ config.templates.inject({}) do |merge_template, template|
16
+ raise(Pec::Errors::UserData, "template:#{template} is not fond!") unless FileTest.exist?("user_datas/#{template}")
17
+ merge_template.merge!(YAML.load_file("user_datas/#{template}").to_hash)
22
18
  end if config.templates
23
- merge_template
24
19
  end
25
20
 
26
21
  def make_port_content(config, ports)
27
22
  config.networks.map do |ether|
28
23
  port_content = {}
29
- port_content["bootproto"] = ether.bootproto
30
- port_content["name"] = ether.name unless ether.options.key?('name')
31
- port_content["name"] = ether.name unless ether.options.key?('name')
32
- port_content["device"] = ether.name unless ether.options.key?('device')
33
- port_content["type"] = 'Ethernet' unless ether.options.key?('type')
34
- port_content["onboot"] = "yes" unless ether.options.key?('onboot')
24
+ %w(name device).each do |k|
25
+ port_content[k] = ether.name unless ether.options.key?(k)
26
+ end
35
27
 
36
- _path = "/etc/sysconfig/network-scripts/ifcfg-#{ether.name}" unless ether.options.key?('path')
28
+ port_content["bootproto"] = ether.bootproto
29
+ port_content["type"] = ether.options['type'] ||'Ethernet'
30
+ port_content["onboot"] = ether.options['onboot'] || 'yes'
31
+ path = ether.options['path'] || "/etc/sysconfig/network-scripts/ifcfg-#{ether.name}"
37
32
 
38
33
  port = ports.find {|p| p.name == ether.name}
34
+ port_content["hwaddr"] = port.mac_address
39
35
 
40
- if port
41
- if ether.bootproto == "static"
42
- port_content["netmask"] = port.netmask
43
- port_content["ipaddr"] = port.ip_address
44
- end
45
- port_content["hwaddr"] = port.mac_address
36
+ if ether.bootproto == "static"
37
+ port_content["netmask"] = port.netmask
38
+ port_content["ipaddr"] = port.ip_address
46
39
  end
47
40
  port_content.merge!(ether.options)
48
41
  {
49
42
  'content' => port_content.map {|k,v| "#{k.upcase}=#{v}"}.join("\n"),
50
43
  'owner' => "root:root",
51
- 'path' => _path,
44
+ 'path' => path,
52
45
  'permissions' => "0644"
53
46
  }
54
47
  end
@@ -0,0 +1,12 @@
1
+ module Pec
2
+ module Errors
3
+ class Error < StandardError; end
4
+ class Ethernet < Error; end
5
+ class Subnet < Error; end
6
+ class Port < Error; end
7
+ class Host < Error; end
8
+ class Query < Error; end
9
+ class UserData < Error; end
10
+ class Configure < Error; end
11
+ end
12
+ end
@@ -28,7 +28,7 @@ module Pec
28
28
  when !exists?
29
29
  create(ip)
30
30
  when used?
31
- false
31
+ raise(Pec::Errors::Port, "ip:#{ip.to_addr} is used!")
32
32
  end
33
33
  end
34
34
 
@@ -89,18 +89,11 @@ module Pec
89
89
  @@use_ip_list << response.data[:body]["port"]["fixed_ips"][0]["ip_address"]
90
90
  response.data[:body]["port"]["id"]
91
91
  end
92
-
93
- rescue Excon::Errors::Error => e
94
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
95
- false
96
92
  end
97
93
 
98
94
  def delete(ip)
99
95
  port = fetch(ip.to_addr)
100
96
  response = Fog::Network[:openstack].delete_port(port["id"]) if port
101
- rescue Excon::Errors::Error => e
102
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
103
- false
104
97
  end
105
98
 
106
99
  def replace(ip)
@@ -3,7 +3,9 @@ module Pec
3
3
  class Subnet
4
4
  include Query
5
5
  def fetch(cidr)
6
- list.find {|p| p["cidr"] == cidr }
6
+ subnet = list.find {|p| p["cidr"] == cidr }
7
+ raise(Pec::Errors::Subnet, "cidr:#{cidr} is not fond!") unless subnet
8
+ subnet
7
9
  end
8
10
  end
9
11
  end
@@ -24,7 +24,9 @@ module Pec
24
24
  end
25
25
 
26
26
  def get_ref(name)
27
+ class_name = self.class.name.demodulize.downcase
27
28
  response = fetch(name)
29
+ raise(Pec::Errors::Query, "#{class_name}:#{name} ref is not fond!") unless response
28
30
  response["links"][0]["href"]
29
31
  end
30
32
  end
@@ -1,3 +1,3 @@
1
1
  module Pec
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -15,47 +15,28 @@ module Pec
15
15
  end
16
16
 
17
17
  ports = get_ports(config)
18
- flavor_ref = get_flavor(config.flavor)
19
- image_ref = get_image(config.image)
18
+ flavor_ref = @flavor.get_ref(config.flavor)
19
+ image_ref = @image.get_ref(config.image)
20
+ options = { "user_data" => Pec::Configure::UserData.make(config, ports) }
20
21
 
21
- return false unless flavor_ref && image_ref
22
-
23
- options = {
24
- "user_data" => Pec::Configure::UserData.make(config, ports),
25
- }
26
22
  @compute.create(config.name, image_ref, flavor_ref, ports, options)
27
23
  end
28
24
 
29
25
  def get_ports(config)
30
26
  config.networks.map do |ether|
31
- ip = IP.new(ether.ip_address)
32
- _subnet = get_subnet(ip)
33
- _port = Pec::Network::Port.new(ether.name, ip.to_addr, _subnet, get_security_group_id(config.security_group)) if _subnet
34
-
35
- unless _port.assign!(ip)
36
- puts "ip addess:#{ip.to_addr} can't create port!"
37
- return false
27
+ begin
28
+ ip = IP.new(ether.ip_address)
29
+ rescue ArgumentError => e
30
+ raise(Pec::Errors::Port, "ip:#{ether.ip_address} #{e}")
38
31
  end
39
- _port
40
- end if config.networks
41
- end
42
32
 
43
- def get_subnet(ip)
44
- _subnet = @subnet.fetch(ip.network.to_s)
45
- puts "ip addess:#{ip.to_addr} subnet not fond!" if _subnet.nil?
46
- _subnet
47
- end
48
-
49
- def get_flavor(name)
50
- flavor_ref = @flavor.get_ref(name)
51
- puts "flavor:#{name} not fond!" if flavor_ref.nil?
52
- flavor_ref
53
- end
33
+ subnet = @subnet.fetch(ip.network.to_s)
34
+ raise(Pec::Errors::Subnet, "subnet:#{ip.network.to_s} is not fond!") unless subnet
54
35
 
55
- def get_image(name)
56
- image_ref = @image.get_ref(name)
57
- puts "image:#{name} not fond!" if image_ref.nil?
58
- image_ref
36
+ port = Pec::Network::Port.new(ether.name, ip.to_addr, subnet, get_security_group_id(config.security_group))
37
+ raise(Pec::Errors::Port, "ip addess:#{ip.to_addr} can't create port!") unless port.assign!(ip)
38
+ port
39
+ end if config.networks
59
40
  end
60
41
 
61
42
  def get_security_group_id(security_groups)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - kazuhiko yamashita
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-08 00:00:00.000000000 Z
11
+ date: 2015-06-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: openstac vm booter.
14
14
  email:
@@ -20,6 +20,7 @@ executables:
20
20
  extensions: []
21
21
  extra_rdoc_files: []
22
22
  files:
23
+ - ".coveralls.yml"
23
24
  - ".gitignore"
24
25
  - ".travis.yml"
25
26
  - Gemfile
@@ -40,6 +41,7 @@ files:
40
41
  - lib/pec/configure/host.rb
41
42
  - lib/pec/configure/sample.rb
42
43
  - lib/pec/configure/user_data.rb
44
+ - lib/pec/errors.rb
43
45
  - lib/pec/network/port.rb
44
46
  - lib/pec/network/subnet.rb
45
47
  - lib/pec/query.rb
@@ -66,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
68
  version: '0'
67
69
  requirements: []
68
70
  rubyforge_project:
69
- rubygems_version: 2.4.6
71
+ rubygems_version: 2.4.7
70
72
  signing_key:
71
73
  specification_version: 4
72
74
  summary: openstack vm booter.