pec 0.4.4 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8c854b8e393a921d53b14b6faa4704c69179e89f
4
- data.tar.gz: b5e10b9145475986f2f59b23fb762bcac456bd1b
3
+ metadata.gz: 8a7355209a3c35e40ac68d514cae1e0ed75e8102
4
+ data.tar.gz: ea45ced7c65397076d2c7496eb5efe44ffa6d41a
5
5
  SHA512:
6
- metadata.gz: fc62df875382a1a9e46f84592212f69ec4c015132aa2804f8b698084b3af401218c6c005df8cf41dc7442bde7cd85910f3c1221dcc394c06f4c2063839fdc009
7
- data.tar.gz: aa06178e8af7c13cb6019ee4db98624f873be9ab1e2368730522ce5942e548348d788162f71d2a1a34be1af2270f2c9b3c2f822d9576df782e01011aeeff040c
6
+ metadata.gz: 3f40e9678fbd05ffeac16fac0ca9eb1832185a38526a188149a473dee6d8e397d1250b1c081a30b687262fb02e7ebab83b02c53dcca3d9b03f7cb4f15daffdb8
7
+ data.tar.gz: 57feae3668f00d475f1b1be8871294caa4fb7ac72ace25a78a9af76e57220cf1dd70dcf58e4aa7e5b5392e20db6c782cf49fd11459c664c44046697d56accc1d
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  /*.yaml
12
12
  *.sample
13
13
  /user_datas
14
+ .ruby-version
data/README.md CHANGED
@@ -37,6 +37,7 @@ _default_: &def
37
37
  tenant: your_tenant
38
38
  image: centos-7.1_chef-12.3_puppet-3.7
39
39
  flavor: m1.small
40
+ availability_zone: nova
40
41
 
41
42
  pyama-test001:
42
43
  <<: *def
@@ -74,8 +75,10 @@ pyama-test002:
74
75
  | flavor | フレーバー名 | ○ | m1.small |
75
76
  | networks | ネットワーク定義 | - | [] |
76
77
  | security_group | セキュリティグループ名 | - | [default,ssh] |
77
- | templates | `user_data`のテンプレート.`./user_data`に配置 | - | [base.yaml,webserver.yaml] |
78
+ | templates | `user_data`のテンプレート `./user_data` に配置 | - | [base.yaml,webserver.yaml] |
78
79
  | user_data | cloud-init記法に準拠 | - | - |
80
+ | availability_zone | アベイラビリティゾーン | - | nova |
81
+
79
82
  * 先頭が_で開始されるインスタンス名はyaml merge記法用途と認識し、スキップします
80
83
 
81
84
  ##### Networks
data/lib/pec.rb CHANGED
@@ -2,42 +2,45 @@ require 'fog'
2
2
  require 'ip'
3
3
  require 'colorator'
4
4
  require "pec/version"
5
- require "pec/query"
6
- require "pec/errors"
7
- require "pec/init"
8
- require "pec/resource"
9
- require "pec/resource/openstack"
10
- require "pec/resource/mock"
11
- require "pec/director"
12
- require "pec/director/helper"
13
- require "pec/director/make_director"
14
- require "pec/director/destroy_director"
15
- require "pec/director/vm_status_director"
5
+ require "pec/logger"
16
6
  require "pec/configure"
17
- require "pec/configure/sample"
18
- require "pec/configure/host"
19
- require "pec/configure/ethernet"
20
- require "pec/configure/user_data"
21
- require "pec/compute/server"
22
- require "pec/compute/flavor"
23
- require "pec/compute/image"
24
- require "pec/compute/tenant"
25
- require "pec/network/security_group"
26
- require "pec/network/port"
27
- require "pec/network/port_state"
28
- require "pec/network/subnet"
7
+ require "pec/director"
8
+ require "pec/builder/server"
9
+ require "pec/builder/port"
10
+ require "pec/builder/user_data"
29
11
  require "pec/cli"
30
12
 
31
13
  module Pec
32
- end
14
+ def self.compute
15
+ @_compute ||= Fog::Compute.new({
16
+ provider: 'openstack'
17
+ })
18
+ @_compute
19
+ end
20
+
21
+ def self.neutron
22
+ @_neutron ||= Fog::Network.new({
23
+ provider: 'openstack'
24
+ })
25
+ @_neutron
26
+ end
27
+
28
+ def self.identity
29
+ @_identity ||= Fog::Identity.new({
30
+ provider: 'openstack'
31
+ })
32
+ @_identity
33
+ end
33
34
 
34
- class ::Hash
35
- def deep_merge(second)
36
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
37
- self.merge(second.to_h, &merger)
38
- end
39
- def deep_merge!(second)
40
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
41
- self.merge!(second.to_h, &merger)
35
+ def self.load_config(file_name=nil)
36
+ file_name ||= 'Pec.yaml'
37
+ @_configure = []
38
+ YAML.load_file(file_name).to_hash.reject {|c| c[0].to_s.match(/^_/)}.each do |host|
39
+ @_configure << Pec::Configure.new(host)
42
40
  end
41
+ end
42
+
43
+ def self.configure
44
+ @_configure
45
+ end
43
46
  end
@@ -0,0 +1,120 @@
1
+ module Pec
2
+ module Builder
3
+ class Port
4
+ attr_reader :user_data
5
+ def build(host)
6
+ ports = []
7
+ @user_data = []
8
+
9
+ host.networks.each do |network|
10
+ validate(network)
11
+ Pec::Logger.notice "port create start : #{network[0]}"
12
+ port = create_port(host, network)
13
+ Pec::Logger.notice "assgin ip : #{port.fixed_ips.first["ip_address"]}"
14
+ ports << port
15
+ @user_data << gen_user_data(network, port)
16
+ end
17
+ {
18
+ nics: ports.map {|port| { port_id: port.id }}
19
+ }
20
+ end
21
+
22
+ def validate(network)
23
+ %w(
24
+ bootproto
25
+ ip_address
26
+ ).each do |k|
27
+ raise "network key #{k} is require" unless network[1][k]
28
+ end
29
+ end
30
+
31
+ def create_port(host, network)
32
+ ip = IP.new(network[1]['ip_address'])
33
+ subnet = Pec.neutron.subnets.find {|s|s.cidr == ip.network.to_s}
34
+ attribute = gen_port_attribute(host, network, subnet, ip)
35
+ Pec.neutron.ports.create(attribute)
36
+ end
37
+
38
+ def gen_port_attribute(host, network, subnet, ip)
39
+ attribute = {
40
+ name: network[0],
41
+ network_id: subnet.network_id
42
+ }
43
+
44
+ attribute.merge!(
45
+ fixed_ip(subnet, ip)
46
+ ) if ip.to_s != subnet.cidr
47
+
48
+ attribute.merge!(
49
+ security_group(host)
50
+ ) if host.security_group
51
+
52
+ attribute.merge!(
53
+ allowed_address_pairs(network)
54
+ ) if network[1]['allowed_address_pairs']
55
+ attribute
56
+ end
57
+
58
+ def gen_user_data(network, port)
59
+ path = network[1]['path'] || "/etc/sysconfig/network-scripts/ifcfg-#{port.name}"
60
+ {
61
+ 'content' => ifcfg_config(network, port),
62
+ 'owner' => "root:root",
63
+ 'path' => path,
64
+ 'permissions' => "0644"
65
+ }
66
+ end
67
+
68
+ def ifcfg_config(network, port)
69
+ base = {
70
+ "name" => port.name,
71
+ "device" => port.name,
72
+ "type" => 'Ethernet',
73
+ "onboot" => 'yes',
74
+ "hwaddr" => port.mac_address
75
+ }
76
+
77
+ base.merge!(
78
+ {
79
+ "netmask" => IP.new(network[1]['ip_address']).netmask.to_s,
80
+ "ipaddr" => port.fixed_ips.first['ip_address'].split("/").first
81
+ }
82
+ ) if network[1]['bootproto'] == "static"
83
+
84
+ # delete options
85
+ %w(allowed_address_pairs ip_address).each {|name| network[1].delete(name)}
86
+
87
+ base.merge!(
88
+ network[1]
89
+ )
90
+
91
+ base.map {|k,v| "#{k.upcase}=#{v}"}.join("\n")
92
+ end
93
+
94
+ #
95
+ # after port options
96
+ #
97
+ def fixed_ip(subnet, ip)
98
+ {
99
+ fixed_ips: [
100
+ { subnet_id: subnet.id, ip_address: ip.to_addr}
101
+ ]
102
+ }
103
+ end
104
+
105
+ def security_group(host)
106
+ ids = host.security_group.map do |name|
107
+ Pec.neutron.security_groups.find {|sg| sg.name == name}.id
108
+ end
109
+ { security_groups: ids }
110
+ end
111
+
112
+ def allowed_address_pairs(network)
113
+ pairs = network[1]['allowed_address_pairs'].map do |pair|
114
+ { ip_address: pair['ip_address'] }
115
+ end
116
+ { allowed_address_pairs: pairs }
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,28 @@
1
+ module Pec
2
+ module Builder
3
+ class Server
4
+ def build(host)
5
+ Pec::Logger.notice "flavor is #{host.flavor}"
6
+ Pec::Logger.notice "image is #{host.image}"
7
+ hash = {
8
+ name: host.name,
9
+ flavor_ref: fetch_flavor(host).id,
10
+ image_ref: fetch_image(host).id
11
+ }
12
+ hash[:availability_zone] = host.availability_zone if host.availability_zone
13
+ hash
14
+ end
15
+
16
+ def self.resource(name)
17
+ define_method("fetch_#{name}", -> (host) {
18
+ r = Pec.compute.send("#{name}s").find {|val|val.name == host.send(name)}
19
+ raise "not fond #{name} #{host.send(name)}" unless r
20
+ r
21
+ })
22
+ end
23
+
24
+ resource 'flavor'
25
+ resource 'image'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Pec
2
+ module Builder
3
+ class UserData
4
+ def build(host, port_user_data)
5
+ user_data = default(host)
6
+ user_data["write_files"] = port_user_data if port_user_data
7
+ if template = load_template(host)
8
+ user_data.deep_merge!(template)
9
+ end
10
+ { user_data: "#cloud-config\n" + user_data.to_yaml }
11
+ end
12
+
13
+ def load_template(host)
14
+ host.templates.inject({}) do |merge_template, template|
15
+ template.to_s.concat('.yaml') unless template.to_s.match(/.*\.yaml/)
16
+ raise "#{template} not fond!" unless FileTest.exist?("user_data/#{template}")
17
+ merge_template.deep_merge!(YAML.load_file("user_data/#{template}").to_hash)
18
+ end if host.templates
19
+ end
20
+
21
+ def default(host)
22
+ _def = host.user_data || {}
23
+ _def['fqdn'] = host.name if host.user_data && !host.user_data['fqdn']
24
+ _def
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ class ::Hash
31
+ def deep_merge(second)
32
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
33
+ self.merge(second.to_h, &merger)
34
+ end
35
+ def deep_merge!(second)
36
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
37
+ self.merge!(second.to_h, &merger)
38
+ end
39
+ end
@@ -12,19 +12,19 @@ module Pec
12
12
 
13
13
  desc 'up', 'create vm by Pec.yaml'
14
14
  def up(host_name = nil)
15
- Pec::Director.execute("make", host_name)
15
+ Pec::Director.make(host_name)
16
16
  end
17
17
 
18
18
  option :force , type: :boolean, aliases: "-f"
19
19
  desc "destroy", "delete vm"
20
20
  def destroy(host_name = nil)
21
- Pec::Director.execute("destroy", host_name, options)
21
+ Pec::Director.destroy(host_name, options)
22
22
  end
23
23
 
24
24
  desc "status", "vm status"
25
25
  def status(host_name = nil)
26
26
  say("Current machine stasus:", :yellow)
27
- Pec::Director.execute("vm_status", host_name)
27
+ Pec::Director.status(host_name)
28
28
  end
29
29
  end
30
30
  end
@@ -1,36 +1,27 @@
1
- require 'yaml'
2
1
  module Pec
3
2
  class Configure
4
- include Enumerable
5
-
6
- def initialize(file_name)
7
- @configure = []
8
-
9
- if file_name.is_a?(Hash)
10
- hash = file_name
11
- else
12
- hash = YAML.load_file(file_name).to_hash
13
- end
14
-
15
- hash.reject {|c| c[0].to_s.match(/^_/)}.each do |config|
16
-
17
- config[1]['user_data'] ||= {}
18
- config[1]['user_data']['fqdn'] ||= config[0].to_s
3
+ def initialize(config)
4
+ validate(config)
5
+ @_config = config
6
+ end
19
7
 
20
- host = Pec::Configure::Host.new(config)
21
- @configure << host if host
22
- end
23
- rescue Psych::SyntaxError,NoMethodError => e
24
- raise(Pec::Errors::Configure, e)
8
+ def name
9
+ @_config[0]
25
10
  end
26
11
 
27
- def filter_by_host(host_name)
28
- @configure.select {|h| host_name.nil? || host_name == h.name}
12
+ def method_missing(method, *args)
13
+ nil unless @_config[1][method.to_s]
14
+ @_config[1][method.to_s]
29
15
  end
30
16
 
31
- def each
32
- @configure.each do |config|
33
- yield config
17
+ def validate(host)
18
+ %w(
19
+ tenant
20
+ image
21
+ flavor
22
+ networks
23
+ ).each do |k|
24
+ raise "host key #{k} is require" unless host[1][k]
34
25
  end
35
26
  end
36
27
  end
@@ -1,57 +1,116 @@
1
1
  module Pec
2
- class Director
3
- class << self
4
-
5
- def execute(action, host_name, options=nil)
6
- config = Pec::Configure.new("Pec.yaml")
7
- director = assign_director(action, options)
8
- config.filter_by_host(host_name).each do |host|
9
- begin
10
- director.execute!(host) if director.do_it?(host)
11
- rescue Pec::Errors::Error => e
12
- director.err_message(e, host)
13
- rescue Excon::Errors::SocketError => e
14
- err_message(e)
15
- rescue Excon::Errors::Error => e
16
- excon_err_message(e)
17
- end
18
- end if config
19
-
20
- rescue Pec::Errors::Configure => e
21
- config_load_err_message(e)
22
- rescue Pec::Errors::Error => e
23
- err_message(e)
24
- rescue Errno::ENOENT => e
25
- err_message(e)
2
+ class Director
3
+ def self.make(host_name)
4
+ Pec.load_config
5
+ Pec.configure.each do |host|
6
+ next if host_name && host.name != host_name
7
+ Pec::Logger.info "make start #{host.name}"
8
+
9
+ Pec.compute.set_tenant(host.tenant)
10
+ Pec.neutron.set_tenant_patch(host.tenant)
11
+
12
+ port_builder = Pec::Builder::Port.new
13
+ server_builder = Pec::Builder::Server.new
14
+ user_data_builder = Pec::Builder::UserData.new
15
+
16
+ attribute = {}
17
+ attribute.merge!(server_builder.build(host))
18
+ attribute.merge!(port_builder.build(host))
19
+
20
+ if user_data = user_data_builder.build(host, port_builder.user_data)
21
+ attribute.merge!(user_data)
22
+ end
23
+
24
+ Pec::Logger.info "create success! #{host.name}" if Pec.compute.servers.create(attribute)
26
25
  end
27
26
 
28
- def assign_director(action, options)
29
- case
30
- when action == "make"
31
- Pec::Director::MakeDirector.new
32
- when action == "destroy"
33
- Pec::Director::DestroyDirector.new(options)
34
- when action == "vm_status"
35
- Pec::Director::VmStatusDirector.new
36
- else
37
- raise
27
+ rescue Excon::Errors::Error => e
28
+ excon_err_message(e)
29
+ rescue => e
30
+ Pec::Logger.critical(e)
31
+ end
32
+
33
+ def self.destroy(host_name, options)
34
+ Pec.load_config
35
+ Pec.configure.each do |host|
36
+ next if host_name && host.name != host_name
37
+ Pec.compute.set_tenant(host.tenant)
38
+
39
+ server = Pec.compute.servers.find {|s|s.name == host.name}
40
+ unless server
41
+ Pec::Logger.notice "not be created #{host.name}"
42
+ next
43
+ end
44
+
45
+ if options[:force] || Thor.new.yes?("#{host.name}: Are you sure you want to destroy the '#{host.name}' VM? [y/N]")
46
+ Pec::Logger.info "#{host.name} is deleted!" if Pec.compute.servers.destroy(server.id)
38
47
  end
39
48
  end
49
+ rescue Excon::Errors::Error => e
50
+ excon_err_message(e)
51
+ rescue => e
52
+ Pec::Logger.critical(e)
53
+ end
54
+
55
+ def self.status(host_name)
56
+ Pec.load_config
57
+ Pec.configure.each do |host|
58
+ next if host_name && host.name != host_name
59
+ server = Pec.compute.servers.find {|s|s.name == host.name}
60
+ if server
61
+ puts sprintf(" %-35s %-10s %-10s %-10s %-10s %-35s %-48s",
62
+ host.name,
63
+ server.state,
64
+ Pec.identity.tenants.find_by_id(server.tenant_id),
65
+ Pec.compute.flavors.get(server.flavor['id']).name,
66
+ server.availability_zone,
67
+ server.os_ext_srv_attr_host,
68
+ server.addresses.map do |net, ethers|
69
+ ethers.map do |ether|
70
+ ether["addr"]
71
+ end
72
+ end.flatten.join(",")
73
+ )
40
74
 
41
- def err_message(e)
42
- puts e.to_s.magenta
75
+ else
76
+ puts sprintf(" %-35s %-10s",
77
+ host.name,
78
+ "uncreated"
79
+ )
80
+ end
43
81
  end
44
82
 
45
- def config_load_err_message(e)
46
- puts e
47
- puts "can't load configfile".magenta
83
+ rescue Excon::Errors::Error => e
84
+ excon_err_message(e)
85
+ rescue => e
86
+ Pec::Logger.critical(e)
87
+ end
88
+
89
+ def self.excon_err_message(e)
90
+ if e.response
91
+ JSON.parse(e.response[:body]).each { |e,m| Pec::Logger.critical("#{e}:#{m["message"]}") }
92
+ else
93
+ Pec::Logger.critical(e)
48
94
  end
95
+ end
96
+ end
97
+ end
49
98
 
50
- def excon_err_message(e)
51
- if e.response
52
- JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}".magenta }
53
- else
54
- puts e
99
+ module Fog
100
+ module Network
101
+ class OpenStack
102
+ class Real
103
+ def set_tenant_patch(tenant)
104
+ @openstack_must_reauthenticate = true
105
+ @openstack_tenant = tenant.to_s
106
+ authenticate
107
+ @path.sub!(/\/$/, '')
108
+ unless @path.match(SUPPORTED_VERSIONS)
109
+ @path = "/" + Fog::OpenStack.get_supported_version(SUPPORTED_VERSIONS,
110
+ @openstack_management_uri,
111
+ @auth_token,
112
+ @connection_options)
113
+ end
55
114
  end
56
115
  end
57
116
  end