pec 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c3a835c64ff11809c32ad8886aa25a8d5bf3600
4
- data.tar.gz: f8a8e8fe3805263d737c63fb54249353ce2d32f5
3
+ metadata.gz: a23835f633410abdb1ca9c614c3d8e386101c17d
4
+ data.tar.gz: 7f3df00c0a75012a93898c33bc9b5f28eb213fa5
5
5
  SHA512:
6
- metadata.gz: 04cd2b3871e58d6cfa4e0696ee318125b1bd7a22ded88fd4a4a75569c41503cc6d409474bbed67780e86881742e94e266bb116b6a9bc0745d831f4529e90f5e5
7
- data.tar.gz: b425427f4e0328fb8e6737f2b1c3c057788ae8519fa2112529c3399fe155db65b5b46747768c645cda188934b97a6b88f7d15fb0666136926945b4694f3f1a69
6
+ metadata.gz: 2f05e20813ffd29318f0ae79608907412682b6aaa52371b7dce7d648351c470f4aba4aeea632020d5dc8b7ab85f6a7c02d2efff21619a33810c13703e6576557
7
+ data.tar.gz: 24b4f770903b98f04c28896c749d8fc784f4896ef2eb13c45cab6db9cc402ee8073d623e97492279dd0a7ce0579d5f1e99746a17f1b0e3cbe803d93d7a6390f7
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
  /tmp/
10
10
  /vendor
11
11
  *.yaml
12
+ *.sample
13
+ /user_datas
data/Gemfile CHANGED
@@ -2,3 +2,13 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in pec.gemspec
4
4
  gemspec
5
+ gem 'fog', :git => 'git@github.com:pyama86/fog.git', :branch => 'support-security-groups'
6
+ gem 'thor'
7
+ gem 'ruby-ip'
8
+ gem 'activesupport'
9
+ gem "bundler"
10
+
11
+ group :development, :test do
12
+ gem "rspec"
13
+ gem "rake"
14
+ end
data/README.md CHANGED
@@ -38,13 +38,13 @@ pyama-test001:
38
38
  networks:
39
39
  eth0:
40
40
  bootproto: static
41
- ip_address: 157.7.190.128/26
42
- gateway: 157.7.190.129
41
+ ip_address: 10.1.1.1/24
42
+ gateway: 10.1.1.254
43
43
  dns1: 8.8.8.8
44
44
  dns2: 8.8.8.8
45
45
  eth1:
46
46
  bootproto: static
47
- ip_address: 10.51.113.0/24
47
+ ip_address: 20.2.2.0/24
48
48
  dns1: 8.8.8.8
49
49
  dns2: 8.8.8.8
50
50
  user_data:
@@ -57,16 +57,13 @@ pyama-test002:
57
57
  networks:
58
58
  eth0:
59
59
  bootproto: static
60
- ip_address: 157.7.190.128/26
61
- gateway: 157.7.190.129
60
+ ip_address: 10.0.0.64/26
61
+ gateway: 10.0.0.127
62
62
  dns1: 8.8.8.8
63
63
  dns2: 8.8.8.8
64
64
  path: /etc/sysconfig/network-scripts/ifcfg-bond0
65
65
  eth1:
66
- bootproto: static
67
- ip_address: 10.51.113.0/24
68
- dns1: 8.8.8.8
69
- dns2: 8.8.8.8
66
+ bootproto: dhcp
70
67
  user_data:
71
68
  hostname: pyama-test002
72
69
  fqdn: pyama-test002.ikemen.com
data/lib/pec.rb CHANGED
@@ -4,6 +4,7 @@ require "pec/version"
4
4
  require "pec/query"
5
5
  require "pec/vm_director"
6
6
  require "pec/configure"
7
+ require "pec/configure/sample"
7
8
  require "pec/configure/host"
8
9
  require "pec/configure/ethernet"
9
10
  require "pec/configure/user_data"
@@ -2,20 +2,43 @@ require 'pec'
2
2
  require 'thor'
3
3
  module Pec
4
4
  class CLI < Thor
5
- option :filename, type: :string, aliases: "-f"
5
+
6
+ desc 'init', 'create sample config'
7
+ def init
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
+
15
+ open("#{dirname}/web_server.yaml.sample","w") do |e|
16
+ YAML.dump(Pec::Configure::Sample.user_data, e)
17
+ end if FileTest.exist?(dirname)
18
+
19
+ end
20
+
6
21
  desc 'up', 'create vm by Pec.yaml'
7
22
  def up(host_name = nil)
8
23
  config = Pec::Configure.new
9
24
  config.load("Pec.yaml")
25
+
10
26
  director = Pec::VmDirector.new
11
27
  config.each do |host|
12
28
  next if !host_name.nil? && host.name != host_name
13
29
  puts "can't create server:#{host.name}" unless director.make(host)
14
30
  end if config
15
31
  end
32
+ option :force , type: :boolean, aliases: "-f"
16
33
  desc "destroy", "delete vm"
17
- def destroy(name)
18
- Pec::Compute::Server.new.destroy!(name) if yes?("#{name}: Are you sure you want to destroy the '#{name}' VM? [y/N]")
34
+ def destroy(name = nil)
35
+ config = Pec::Configure.new
36
+ config.load("Pec.yaml")
37
+
38
+ config.each do |host|
39
+ 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"]
41
+ end if config
19
42
  end
20
43
  end
21
44
  end
@@ -2,6 +2,14 @@ module Pec
2
2
  class Compute
3
3
  class Security_Group
4
4
  include Query
5
+ def add_security_group(server_id, security_groups)
6
+ security_groups.each do |sg_name|
7
+ response = Fog::Compute[:openstack].add_security_group(server_id, sg_name)
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
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -4,26 +4,26 @@ module Pec
4
4
  class Server
5
5
  include Query
6
6
  def create(name, image_ref, flavor_ref, ports, options)
7
- if exists?(name)
8
- puts "skip create server! name:#{name} is exists!"
9
- return true
10
- end
11
-
12
7
  networks = ports.map do |port|
13
8
  if port.used?
14
9
  puts "port-id:#{port.id} ip-addr:#{port.ip_address} in used"
15
10
  return false
16
11
  end
17
- { net_id: port.network_id, fixed_ip: port.ip_address, port_id: port.id }
12
+ puts "#{name}: assingn ip #{port.ip_address}"
13
+ { port_id: port.id }
18
14
  end if ports
19
15
 
20
16
  options.merge!({ 'nics' => networks })
17
+
21
18
  response = Fog::Compute[:openstack].create_server(name, image_ref, flavor_ref, options)
19
+
22
20
  if response[:status] == 202
23
21
  puts "success create for server_name:#{name}"
24
22
  end
25
- true
26
- rescue Excon::Errors::Conflict,Excon::Errors::BadRequest => e
23
+
24
+ response.data[:body]["server"]["id"]
25
+
26
+ rescue Excon::Errors::Error => e
27
27
  JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
28
28
  false
29
29
  end
@@ -40,9 +40,14 @@ module Pec
40
40
  end
41
41
 
42
42
  response = Fog::Compute[:openstack].delete_server(server["id"]) if server
43
+
43
44
  if response && response[:status] == 204
44
45
  puts "server_name:#{server_name} is deleted!"
45
46
  end
47
+
48
+ rescue Excon::Errors::Error => e
49
+ JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
50
+ false
46
51
  end
47
52
  end
48
53
  end
@@ -4,7 +4,7 @@ module Pec
4
4
  attr_reader :name, :bootproto, :ip_address, :options
5
5
  def initialize(config)
6
6
  @name = config[0];
7
- @bootprot = config[1]["bootproto"];
7
+ @bootproto = config[1]["bootproto"];
8
8
  @ip_address = config[1]["ip_address"];
9
9
  @options = config[1].select do |k,v|
10
10
  { k => v } if k != "bootproto" && k != "ip_address"
@@ -17,7 +17,7 @@ module Pec
17
17
  end
18
18
 
19
19
  def check_require_key(name, config)
20
- err = %w(bootproto ip_address).find {|k| !config[1].key?(k)}
20
+ err = %w(bootproto).find {|k| !config[1].key?(k)}
21
21
  return true if err.nil?
22
22
  puts "skip! #{name}: #{err} is required!"
23
23
  false
@@ -1,13 +1,14 @@
1
1
  module Pec
2
2
  class Configure
3
3
  class Host
4
- attr_reader :name, :image, :flavor,:security_group, :user_data, :networks
4
+ attr_reader :name, :image, :flavor,:security_group, :user_data, :networks, :templates
5
5
  def initialize(config)
6
6
  @name = config[0];
7
7
  @image = config[1]["image"];
8
8
  @flavor = config[1]["flavor"];
9
9
  @security_group = config[1]["security_group"];
10
10
  @user_data = config[1]["user_data"];
11
+ @templates = config[1]["templates"]
11
12
  end
12
13
 
13
14
  def append_network(network)
@@ -20,8 +21,7 @@ module Pec
20
21
  host = self.new(config) if check_require_key(config)
21
22
  config[1]["networks"].each do |net|
22
23
  net_config = Pec::Configure::Ethernet.load(config[0], net)
23
- return nil unless net_config
24
- host.append_network(net_config)
24
+ host.append_network(net_config) if net_config
25
25
  end if host && config[1]["networks"]
26
26
  host
27
27
  end
@@ -0,0 +1,54 @@
1
+ module Pec
2
+ class Configure
3
+ class Sample
4
+ class << self
5
+ def pec_file
6
+ {
7
+ "sever_name" => {
8
+ "image" => "centos-7",
9
+ "flavor" => "m1.small",
10
+ "networks" => [
11
+ {
12
+ "eth0" => {
13
+ "bootproto" => "static",
14
+ "ip_address" => "10.0.0.0/24",
15
+ "gateway" => "10.0.0.254",
16
+ "dns1" => "10.0.0.10"
17
+ },
18
+ "eth1" => {
19
+ "bootproto" => "static",
20
+ "ip_address" => "20.0.0.11/24",
21
+ "gateway" => "20.0.0.254",
22
+ "dns1" => "20.0.0.10"
23
+ }
24
+ }
25
+ ],
26
+ "security_group" => [
27
+ "default",
28
+ "www from any"
29
+ ],
30
+ "template" => "web_server.yaml",
31
+ "user_data" => {
32
+ "hostname" => "pec",
33
+ "fqdn" => "pec.pyama.com"
34
+ }
35
+ }
36
+ }
37
+ end
38
+ def user_data
39
+ {
40
+ "hostname" => "pec",
41
+ "fqdn" => "pec.pyama.com",
42
+ "users" => [
43
+ {
44
+ "name" => "centos",
45
+ "groups" => "sudo",
46
+ "shell" => "/bin/sh"
47
+ }
48
+ ]
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -6,12 +6,28 @@ module Pec
6
6
  def make(config, ports)
7
7
  user_data = {}
8
8
  user_data["write_files"] = make_port_content(config, ports) if ports
9
- Base64.encode64("#cloud-config\n" + user_data.merge(config.user_data).to_yaml)
9
+ user_data.merge!(config.user_data) if config.user_data
10
+ user_data.merge!(get_template(config)) if get_template(config)
11
+ Base64.encode64("#cloud-config\n" + user_data.to_yaml)
12
+ end
13
+
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
22
+ end if config.templates
23
+ merge_template
10
24
  end
11
25
 
12
26
  def make_port_content(config, ports)
13
27
  config.networks.map do |ether|
14
28
  port_content = {}
29
+ port_content["bootproto"] = ether.bootproto
30
+ port_content["name"] = ether.name unless ether.options.key?('name')
15
31
  port_content["name"] = ether.name unless ether.options.key?('name')
16
32
  port_content["device"] = ether.name unless ether.options.key?('device')
17
33
  port_content["type"] = 'Ethernet' unless ether.options.key?('type')
@@ -22,9 +38,11 @@ module Pec
22
38
  port = ports.find {|p| p.name == ether.name}
23
39
 
24
40
  if port
25
- port_content["netmask"] = port.netmask
41
+ if ether.bootproto == "static"
42
+ port_content["netmask"] = port.netmask
43
+ port_content["ipaddr"] = port.ip_address
44
+ end
26
45
  port_content["hwaddr"] = port.mac_address
27
- port_content["ipaddr"] = port.ip_address
28
46
  end
29
47
  port_content.merge!(ether.options)
30
48
  {
@@ -2,18 +2,53 @@ require 'json'
2
2
  module Pec
3
3
  class Network
4
4
  class Port
5
+ @@use_ip_list = []
5
6
  attr_reader :name, :subnet
6
7
  include Query
7
- def initialize(name, ip_addr, subnet)
8
+ def initialize(name, ip_addr, subnet, security_groups)
8
9
  @name = name
9
10
  @subnet = subnet
11
+ @security_groups = security_groups
10
12
  @config = fetch(ip_addr)
11
13
  end
12
14
 
15
+ def assign!(ip)
16
+ # dhcp ip recycle
17
+ if request_any_address?(ip)
18
+ free = fetch_free_port
19
+ if free
20
+ ip = IP.new("#{free["fixed_ips"][0]["ip_address"]}/#{ip.pfxlen}")
21
+ @config = free
22
+ end
23
+ end
24
+
25
+ case
26
+ when exists? && !used?
27
+ replace(ip)
28
+ when !exists?
29
+ create(ip)
30
+ when used?
31
+ false
32
+ end
33
+ end
34
+
35
+ def request_any_address?(ip)
36
+ ip.to_s == subnet["cidr"]
37
+ end
38
+
13
39
  def fetch(ip_addr)
14
40
  list.find {|p| p["fixed_ips"][0]["ip_address"] == ip_addr }
15
41
  end
16
42
 
43
+ def fetch_free_port
44
+ list.find do |p|
45
+ p["fixed_ips"][0]["subnet_id"] == @subnet["id"] &&
46
+ p["device_owner"].empty? &&
47
+ p["admin_state_up"] &&
48
+ !@@use_ip_list.include?(p["fixed_ips"][0]["ip_address"])
49
+ end
50
+ end
51
+
17
52
  def exists?
18
53
  !@config.nil?
19
54
  end
@@ -43,36 +78,34 @@ module Pec
43
78
  end
44
79
 
45
80
  def create(ip)
46
- options = {}
47
- ## parameter is network address to dhcp
81
+ options = { security_groups: @security_groups }
48
82
  if ip.to_s != subnet["cidr"]
49
- options = { fixed_ips: [{ subnet_id: @subnet["id"], ip_address: ip.to_addr}]}
83
+ options.merge!({ fixed_ips: [{ subnet_id: @subnet["id"], ip_address: ip.to_addr}]})
50
84
  end
85
+ response = Fog::Network[:openstack].create_port(@subnet["network_id"], options)
51
86
 
52
- res = Fog::Network[:openstack].create_port(@subnet["network_id"], options)
87
+ if response
88
+ @config = response.data[:body]["port"]
89
+ @@use_ip_list << response.data[:body]["port"]["fixed_ips"][0]["ip_address"]
90
+ response.data[:body]["port"]["id"]
91
+ end
53
92
 
54
- @config = res.data[:body]["port"] if res
55
- @@_list['port'] ||= []
56
- @@_list['port'] << @config
57
- true
58
- rescue Excon::Errors::Conflict => e
93
+ rescue Excon::Errors::Error => e
59
94
  JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
60
95
  false
61
96
  end
62
97
 
63
- def delete(port_id)
64
- Fog::Network[:openstack].delete_port(port_id)
65
- rescue Excon::Errors::Conflict => e
98
+ def delete(ip)
99
+ port = fetch(ip.to_addr)
100
+ response = Fog::Network[:openstack].delete_port(port["id"]) if port
101
+ rescue Excon::Errors::Error => e
66
102
  JSON.parse(e.response[:body]).each { |e,m| puts "#{e}:#{m["message"]}" }
67
103
  false
68
104
  end
69
105
 
70
106
  def replace(ip)
71
- delete(id)
72
- @@_list['port'] = nil
73
- create(ip)
107
+ create(ip) if delete(ip)
74
108
  end
75
-
76
109
  end
77
110
  end
78
111
  end
@@ -2,7 +2,6 @@ require 'active_support/core_ext/string/inflections'
2
2
  require 'fog'
3
3
  module Pec
4
4
  module Query
5
- @@_list = Hash.new
6
5
  def get_adapter
7
6
  case
8
7
  when self.class.name.include?('Network')
@@ -16,7 +15,8 @@ module Pec
16
15
 
17
16
  def list
18
17
  name = self.class.name.demodulize.downcase+"s"
19
- @@_list[name] || get_adapter.send("list_#{name}").data[:body][name]
18
+ @_list ||= Hash.new
19
+ @_list[name] ||= get_adapter.send("list_#{name}").data[:body][name]
20
20
  end
21
21
 
22
22
  def fetch(name)
@@ -1,3 +1,3 @@
1
1
  module Pec
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -9,15 +9,20 @@ module Pec
9
9
  end
10
10
 
11
11
  def make(config)
12
+ if @compute.exists?(config.name)
13
+ puts "skip create server! name:#{config.name} is exists!"
14
+ return true
15
+ end
16
+
12
17
  ports = get_ports(config)
13
18
  flavor_ref = get_flavor(config.flavor)
14
19
  image_ref = get_image(config.image)
20
+
15
21
  return false unless flavor_ref && image_ref
22
+
16
23
  options = {
17
24
  "user_data" => Pec::Configure::UserData.make(config, ports),
18
- "security_groups" => config.security_group
19
25
  }
20
-
21
26
  @compute.create(config.name, image_ref, flavor_ref, ports, options)
22
27
  end
23
28
 
@@ -25,20 +30,9 @@ module Pec
25
30
  config.networks.map do |ether|
26
31
  ip = IP.new(ether.ip_address)
27
32
  _subnet = get_subnet(ip)
28
- return false unless _subnet
29
-
30
- _port = Pec::Network::Port.new(ether.name, ip.to_addr, _subnet)
33
+ _port = Pec::Network::Port.new(ether.name, ip.to_addr, _subnet, get_security_group_id(config.security_group)) if _subnet
31
34
 
32
- res = case
33
- when _port.exists? && !_port.used?
34
- _port.replace(ip)
35
- when !_port.exists?
36
- _port.create(ip)
37
- when _port.used?
38
- false
39
- end
40
-
41
- unless res
35
+ unless _port.assign!(ip)
42
36
  puts "ip addess:#{ip.to_addr} can't create port!"
43
37
  return false
44
38
  end
@@ -63,5 +57,12 @@ module Pec
63
57
  puts "image:#{name} not fond!" if image_ref.nil?
64
58
  image_ref
65
59
  end
60
+
61
+ def get_security_group_id(security_groups)
62
+ security_groups.map do |name|
63
+ sg = @security_group.fetch(name)
64
+ sg["id"] if sg
65
+ end if security_groups
66
+ end
66
67
  end
67
68
  end
@@ -17,10 +17,4 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
- spec.add_dependency 'fog'
21
- spec.add_dependency 'thor'
22
- spec.add_dependency 'ruby-ip'
23
- spec.add_dependency 'activesupport'
24
- spec.add_development_dependency "bundler", "~> 1.9"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
20
  end
metadata CHANGED
@@ -1,99 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
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-07 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: fog
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: thor
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: ruby-ip
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: activesupport
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: bundler
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '1.9'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '1.9'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '10.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '10.0'
11
+ date: 2015-06-08 00:00:00.000000000 Z
12
+ dependencies: []
97
13
  description: openstac vm booter.
98
14
  email:
99
15
  - pyama@pepabo.com
@@ -122,6 +38,7 @@ files:
122
38
  - lib/pec/configure.rb
123
39
  - lib/pec/configure/ethernet.rb
124
40
  - lib/pec/configure/host.rb
41
+ - lib/pec/configure/sample.rb
125
42
  - lib/pec/configure/user_data.rb
126
43
  - lib/pec/network/port.rb
127
44
  - lib/pec/network/subnet.rb