tinet 0.0.2
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 +7 -0
- data/.gitignore +50 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/bin/tinet +5 -0
- data/lib/tinet.rb +8 -0
- data/lib/tinet/cli.rb +119 -0
- data/lib/tinet/command/base.rb +130 -0
- data/lib/tinet/command/build.rb +11 -0
- data/lib/tinet/command/conf.rb +25 -0
- data/lib/tinet/command/down.rb +43 -0
- data/lib/tinet/command/exec.rb +18 -0
- data/lib/tinet/command/init.rb +17 -0
- data/lib/tinet/command/ps.rb +28 -0
- data/lib/tinet/command/pull.rb +16 -0
- data/lib/tinet/command/up.rb +104 -0
- data/lib/tinet/data.rb +54 -0
- data/lib/tinet/link.rb +65 -0
- data/lib/tinet/node.rb +65 -0
- data/lib/tinet/setting.rb +54 -0
- data/lib/tinet/shell.rb +36 -0
- data/lib/tinet/switch.rb +53 -0
- data/lib/tinet/version.rb +3 -0
- data/spec.template.yml +39 -0
- data/tinet.gemspec +32 -0
- metadata +132 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f3759871bec017bb9c62083793496f041da97ea833be5ff9ad337c11fdc50854
|
|
4
|
+
data.tar.gz: 0d61c7aff75feef1ec886e10fdb81a60ba54674fc933d757c63800e2df0081f4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 007d93174d1823b979983739553f7626d9826dba2727270aef246dcbc99d87d46ecb4a00bdf70eebdf7b86fe21fc8eea3429f5ff25399441ff788f58b4d23a78
|
|
7
|
+
data.tar.gz: 5748d97ecbe6812c4bca50dee08b1b0f915f461dfcca5df178e1ba3680000d1205f5ff8f27b0aa898c44043af288047f69b26debbeaae559c8c512652f824afa
|
data/.gitignore
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/spec/examples.txt
|
|
9
|
+
/test/tmp/
|
|
10
|
+
/test/version_tmp/
|
|
11
|
+
/tmp/
|
|
12
|
+
|
|
13
|
+
# Used by dotenv library to load environment variables.
|
|
14
|
+
# .env
|
|
15
|
+
|
|
16
|
+
## Specific to RubyMotion:
|
|
17
|
+
.dat*
|
|
18
|
+
.repl_history
|
|
19
|
+
build/
|
|
20
|
+
*.bridgesupport
|
|
21
|
+
build-iPhoneOS/
|
|
22
|
+
build-iPhoneSimulator/
|
|
23
|
+
|
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
|
25
|
+
#
|
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
|
29
|
+
#
|
|
30
|
+
# vendor/Pods/
|
|
31
|
+
|
|
32
|
+
## Documentation cache and generated files:
|
|
33
|
+
/.yardoc/
|
|
34
|
+
/_yardoc/
|
|
35
|
+
/doc/
|
|
36
|
+
/rdoc/
|
|
37
|
+
|
|
38
|
+
## Environment normalization:
|
|
39
|
+
/.bundle/
|
|
40
|
+
/vendor/bundle
|
|
41
|
+
/lib/bundler/man/
|
|
42
|
+
|
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
45
|
+
# Gemfile.lock
|
|
46
|
+
# .ruby-version
|
|
47
|
+
# .ruby-gemset
|
|
48
|
+
|
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
50
|
+
.rvmrc
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Koki Sato
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# tinet-rb
|
|
2
|
+
|
|
3
|
+
Ruby implement of [slankdev/tinet](https://github.com/slankdev/tinet).
|
|
4
|
+
|
|
5
|
+
:warning: **This is now prototype version. And also it is incompatible with YAML config format in [slankdev/tinet](https://github.com/slankdev/tinet).**
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Docker
|
|
10
|
+
- Open vSwitch
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
$ gem install tinet
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
$ tinet [OPTIONS] COMMAND
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
-f, [--specfile=SPECFILE] # Specify specification YAML file (Default: ./spec.yml)
|
|
25
|
+
-d, [--dry-run] # Print the recipes that are needed to execute
|
|
26
|
+
-v, [--version] # Show the TINET version information
|
|
27
|
+
|
|
28
|
+
Commands:
|
|
29
|
+
tinet build [OPTIONS] # Build Docker images from the spec file
|
|
30
|
+
tinet conf [OPTIONS] # Execute commands in a running container
|
|
31
|
+
tinet down [OPTIONS] # Stop and remove containers
|
|
32
|
+
tinet exec [OPTIONS] NODE COMMAND # Execute a command in a running container
|
|
33
|
+
tinet help [COMMAND] # Describe available commands or one specific command
|
|
34
|
+
tinet init # Generate template spec file
|
|
35
|
+
tinet ps [OPTIONS] # List services
|
|
36
|
+
tinet pull [OPTIONS] # Pull service images
|
|
37
|
+
tinet restart [OPTIONS] # Down and Up running containers
|
|
38
|
+
tinet up [OPTIONS] # Create and start containers
|
|
39
|
+
tinet version # Show the TINET version information
|
|
40
|
+
```
|
data/Rakefile
ADDED
data/bin/tinet
ADDED
data/lib/tinet.rb
ADDED
data/lib/tinet/cli.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "tinet/setting"
|
|
3
|
+
require "tinet/command/build"
|
|
4
|
+
require "tinet/command/conf"
|
|
5
|
+
require "tinet/command/down"
|
|
6
|
+
require "tinet/command/exec"
|
|
7
|
+
require "tinet/command/init"
|
|
8
|
+
require "tinet/command/ps"
|
|
9
|
+
require "tinet/command/pull"
|
|
10
|
+
require "tinet/command/up"
|
|
11
|
+
|
|
12
|
+
module Tinet
|
|
13
|
+
class CLI < Thor
|
|
14
|
+
map %w[--version -v] => :version
|
|
15
|
+
|
|
16
|
+
desc 'init', 'Generate template spec file'
|
|
17
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
18
|
+
def init
|
|
19
|
+
return version if options[:version]
|
|
20
|
+
Tinet::Command::Init.new.run
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc 'ps [OPTIONS]', 'List services'
|
|
24
|
+
option :all, aliases: '-a', type: :boolean, desc: 'Show all containers (default shows just running)'
|
|
25
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
26
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
27
|
+
def ps
|
|
28
|
+
return version if options[:version]
|
|
29
|
+
Tinet::Command::Ps.new(options).run
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc 'up [OPTIONS]', 'Create and start containers'
|
|
33
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
34
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
35
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
36
|
+
def up
|
|
37
|
+
return version if options[:version]
|
|
38
|
+
Tinet::Command::Up.new(options).run
|
|
39
|
+
Tinet::Command::Conf.new(options).run
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc 'down [OPTIONS]', 'Stop and remove containers'
|
|
43
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
44
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
45
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
46
|
+
def down
|
|
47
|
+
return version if options[:version]
|
|
48
|
+
Tinet::Command::Down.new(options).run
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'pull [OPTIONS]', 'Pull service images'
|
|
52
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
53
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
54
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
55
|
+
def pull
|
|
56
|
+
return version if options[:version]
|
|
57
|
+
Tinet::Command::Pull.new(options).run
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc 'exec [OPTIONS] NODE COMMAND', 'Execute a command in a running container'
|
|
61
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
62
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
63
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
64
|
+
def exec(node, command)
|
|
65
|
+
return version if options[:version]
|
|
66
|
+
Tinet::Command::Exec.new(options).run(node, command)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc 'build [OPTIONS]', 'Build Docker images from the spec file'
|
|
70
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
71
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
72
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
73
|
+
def build
|
|
74
|
+
return version if options[:version]
|
|
75
|
+
Tinet::Command::Build.new(options).run
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc 'conf [OPTIONS]', 'Execute commands in a running container'
|
|
79
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
80
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
81
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
82
|
+
def conf
|
|
83
|
+
return version if options[:version]
|
|
84
|
+
Tinet::Command::Conf.new(options).run
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
desc 'restart [OPTIONS]', 'Down and Up running containers'
|
|
88
|
+
option :specfile, aliases: '-f', type: :string, default: Tinet::DEFAULT_SPECFILE_PATH, desc: 'Specify specification YAML file'
|
|
89
|
+
option 'dry-run', aliases: '-d', type: :boolean, default: false, desc: 'Print the recipes that are needed to execute'
|
|
90
|
+
option :version, aliases: '-v', type: :boolean, default: false, desc: 'Show the TINET version information'
|
|
91
|
+
def restart
|
|
92
|
+
return version if options[:version]
|
|
93
|
+
Tinet::Command::Down.new(options).run
|
|
94
|
+
Tinet::Command::Up.new(options).run
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
desc 'version', 'Show the TINET version information'
|
|
98
|
+
def version
|
|
99
|
+
puts "TINET version: #{Tinet::VERSION}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @note Override {Thor#help}
|
|
103
|
+
def help(command = nil, subcommand = false)
|
|
104
|
+
if command.nil?
|
|
105
|
+
puts <<-USAGE
|
|
106
|
+
Usage:
|
|
107
|
+
tinet [OPTIONS] COMMAND
|
|
108
|
+
|
|
109
|
+
Options:
|
|
110
|
+
-f, [--specfile=SPECFILE] # Specify specification YAML file (Default: ./spec.yml)
|
|
111
|
+
-d, [--dry-run] # Print the recipes that are needed to execute
|
|
112
|
+
-v, [--version] # Show the TINET version information
|
|
113
|
+
|
|
114
|
+
USAGE
|
|
115
|
+
end
|
|
116
|
+
super
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require "tinet/data"
|
|
2
|
+
require "tinet/link"
|
|
3
|
+
require "tinet/shell"
|
|
4
|
+
|
|
5
|
+
module Tinet
|
|
6
|
+
module Command
|
|
7
|
+
class Base
|
|
8
|
+
include Shell
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
@options = options
|
|
12
|
+
check_docker_installed unless dry_run
|
|
13
|
+
check_ovs_vsctl_installed unless dry_run
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
|
|
18
|
+
# @return [Logger]
|
|
19
|
+
def logger
|
|
20
|
+
Tinet.logger
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String]
|
|
24
|
+
def specfile
|
|
25
|
+
@options.fetch(:specfile, '')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [boolean]
|
|
29
|
+
def dry_run
|
|
30
|
+
@options.fetch('dry-run', false)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Tinet::Data, nil]
|
|
34
|
+
def data
|
|
35
|
+
return nil if specfile.nil? || specfile.empty?
|
|
36
|
+
@data ||= Tinet::Data.parse(specfile)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [Array<Tinet::Node>, nil]
|
|
40
|
+
def nodes
|
|
41
|
+
data.nodes unless data.nil?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Array<Tinet::Switch>, nil]
|
|
45
|
+
def switches
|
|
46
|
+
data.switches unless data.nil?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Array<Tinet::Link>]
|
|
50
|
+
def links
|
|
51
|
+
return nil if data.nil?
|
|
52
|
+
@links ||= Tinet::Link.link(nodes, switches)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @param name [String]
|
|
56
|
+
# @return [String]
|
|
57
|
+
def namespaced(name)
|
|
58
|
+
"#{Tinet.namespace}-#{name}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @note Override {Tinet::Shell#sh}
|
|
62
|
+
def sh(command, dry_run: dry_run(), print: true, continue: false)
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def mount_docker_netns(container, netns)
|
|
67
|
+
if dry_run
|
|
68
|
+
logger.info "PID=`sudo docker inspect #{container} -f {{.State.Pid}}`"
|
|
69
|
+
pid = '$PID'
|
|
70
|
+
else
|
|
71
|
+
pid, * = sudo "docker inspect #{container} -f {{.State.Pid}}"
|
|
72
|
+
end
|
|
73
|
+
sudo 'mkdir -p /var/run/netns'
|
|
74
|
+
sudo "ln -s /proc/#{pid}/ns/net /var/run/netns/#{netns}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def exec_pre_cmd
|
|
78
|
+
data.options[:pre_cmd].each { |cmd| sudo command }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def exec_pre_init
|
|
82
|
+
data.options[:pre_init].each { |cmd| sudo command }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def exec_post_init
|
|
86
|
+
data.options[:post_init].each { |cmd| sudo command }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def exec_pre_conf
|
|
90
|
+
data.options[:pre_conf].each { |cmd| sudo command }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def exec_post_conf
|
|
94
|
+
data.options[:post_conf].each { |cmd| sudo command }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def exec_pre_down
|
|
98
|
+
data.options[:pre_down].each { |cmd| sudo command }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def exec_post_down
|
|
102
|
+
data.options[:post_down].each { |cmd| sudo command }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
# @return [boolean]
|
|
108
|
+
def command_exist?(command)
|
|
109
|
+
*, status = sh "which #{command}", print: false, continue: true
|
|
110
|
+
status.success?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def check_docker_installed
|
|
114
|
+
unless command_exist?('docker')
|
|
115
|
+
message = 'ERROR: Docker is not installed. TINET requires Docker.'
|
|
116
|
+
logger.error(message)
|
|
117
|
+
exit(1)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def check_ovs_vsctl_installed
|
|
122
|
+
unless command_exist?('ovs-vsctl')
|
|
123
|
+
message = 'ERROR: Open vSwitch is not installed. TINET requires Open vSwitch.'
|
|
124
|
+
logger.error(message)
|
|
125
|
+
exit(1)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Conf < Base
|
|
6
|
+
def run
|
|
7
|
+
exec_pre_cmd
|
|
8
|
+
exec_pre_conf
|
|
9
|
+
|
|
10
|
+
nodes.each do |node|
|
|
11
|
+
node.cmds.each do |cmd|
|
|
12
|
+
case node.type
|
|
13
|
+
when :docker
|
|
14
|
+
sudo "docker exec #{namespaced(node.name)} #{cmd} > /dev/null"
|
|
15
|
+
when :netns
|
|
16
|
+
sudo "ip netns exec #{namespaced(node.name)} #{cmd} > /dev/null"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
exec_post_conf
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Down < Base
|
|
6
|
+
def run
|
|
7
|
+
exec_pre_cmd
|
|
8
|
+
exec_pre_down
|
|
9
|
+
|
|
10
|
+
nodes.each do |node|
|
|
11
|
+
node.interfaces.each do |interface|
|
|
12
|
+
if interface.type == :phys
|
|
13
|
+
detach_physnet_from_docker("#{namespaced(node.name)}", interface.name)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
nodes.each do |node|
|
|
19
|
+
case node.type
|
|
20
|
+
when :docker
|
|
21
|
+
sudo "docker stop #{namespaced(node.name)}"
|
|
22
|
+
when :netns
|
|
23
|
+
sudo "ip netns del #{namespaced(node.name)}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
switches.each do |switch|
|
|
28
|
+
sudo "ovs-vsctl del-br #{namespaced(switch.name)}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
exec_post_down
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def detach_physnet_from_docker(container, ifname)
|
|
37
|
+
mount_docker_netns(container, container)
|
|
38
|
+
sudo "ip netns exec #{container} ip link set #{ifname} netns 1"
|
|
39
|
+
sudo "ip netns del #{container}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Exec < Base
|
|
6
|
+
def run(node_name, command)
|
|
7
|
+
node = nodes.find { |node| node.name == node_name }
|
|
8
|
+
raise "No such container: #{node_name}" if node.nil?
|
|
9
|
+
case node.type
|
|
10
|
+
when :docker
|
|
11
|
+
sudo "docker exec -it #{namespaced(node.name)} #{command}"
|
|
12
|
+
when :netns
|
|
13
|
+
sudo "ip netns exec #{namespaced(node.name)} #{command}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "tinet/setting"
|
|
3
|
+
require "tinet/shell"
|
|
4
|
+
require "tinet/command/base"
|
|
5
|
+
|
|
6
|
+
module Tinet
|
|
7
|
+
module Command
|
|
8
|
+
class Init < Base
|
|
9
|
+
def run
|
|
10
|
+
template = File.join(Tinet::ROOT, 'spec.template.yml')
|
|
11
|
+
specfile = Tinet::DEFAULT_SPECFILE_PATH
|
|
12
|
+
FileUtils.cp(template, specfile)
|
|
13
|
+
logger.info 'Initialized. Check spec.yml'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Ps < Base
|
|
6
|
+
def run
|
|
7
|
+
if all
|
|
8
|
+
stdout, * = sudo "docker ps -a -f name=#{Tinet.namespace}"
|
|
9
|
+
else
|
|
10
|
+
stdout, * = sudo "docker ps -f name=#{Tinet.namespace}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
unless dry_run
|
|
14
|
+
logger.info 'TINET Docker Containers'
|
|
15
|
+
logger.info '-' * 50
|
|
16
|
+
logger.info stdout
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# @return [boolean]
|
|
23
|
+
def all
|
|
24
|
+
@options[:all]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Pull < Base
|
|
6
|
+
def run
|
|
7
|
+
nodes.each do |node|
|
|
8
|
+
unless node.image.nil?
|
|
9
|
+
stdout, * = sudo "docker pull #{node.image}"
|
|
10
|
+
logger.info stdout unless dry_run
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
require "tinet/command/base"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
module Command
|
|
5
|
+
class Up < Base
|
|
6
|
+
def run
|
|
7
|
+
exec_pre_cmd
|
|
8
|
+
exec_pre_init
|
|
9
|
+
|
|
10
|
+
# Create Nodes and Switches
|
|
11
|
+
nodes.each { |node| node_up(node) }
|
|
12
|
+
switches.each { |switch| switch_up(switch) }
|
|
13
|
+
|
|
14
|
+
# Create Links
|
|
15
|
+
links.each { |link| link_up(link) }
|
|
16
|
+
|
|
17
|
+
# Attach physnet / veth to Conitaner
|
|
18
|
+
nodes.each do |node|
|
|
19
|
+
node.interfaces.each do |interface|
|
|
20
|
+
case interface.type
|
|
21
|
+
when :phys
|
|
22
|
+
koko_physnet("#{namespaced(node.name)}", interface.name)
|
|
23
|
+
when :veth
|
|
24
|
+
sudo "ip link add #{interface.name} type veth peer name #{interface.args}"
|
|
25
|
+
koko_physnet("#{namespaced(node.name)}", interface.name)
|
|
26
|
+
sudo "ip link set #{interface.args} up"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
exec_post_init
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def all
|
|
37
|
+
@options[:all]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param node [Tinet::Node]
|
|
41
|
+
def node_up(node)
|
|
42
|
+
case node.type
|
|
43
|
+
when :docker
|
|
44
|
+
sudo "docker run -td --hostname #{node.name} --net=none --name #{namespaced(node.name)} --rm --privileged #{node.image}"
|
|
45
|
+
when :netns
|
|
46
|
+
sudo "ip netns add #{namespaced(node.name)}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @param switch [Tinet::Switch]
|
|
51
|
+
def switch_up(switch)
|
|
52
|
+
sudo "ovs-vsctl add-br #{namespaced(switch.name)}"
|
|
53
|
+
sudo "ip link set #{namespaced(switch.name)} up"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @param link [Tinet::Link]
|
|
57
|
+
def link_up(link)
|
|
58
|
+
left, right = link.left, link.right
|
|
59
|
+
case link.type
|
|
60
|
+
when :n2n
|
|
61
|
+
mount_docker_netns(namespaced(left.node.name), namespaced(left.node.name)) if left.node.type == :docker
|
|
62
|
+
mount_docker_netns(namespaced(right.node.name), namespaced(right.node.name)) if right.node.type == :docker
|
|
63
|
+
sudo "ip link add #{left.name} netns #{namespaced(left.node.name)} type veth peer name #{right.name} netns #{namespaced(right.node.name)}"
|
|
64
|
+
sudo "ip netns exec #{namespaced(left.node.name)} ip link set #{left.name} up"
|
|
65
|
+
sudo "ip netns exec #{namespaced(right.node.name)} ip link set #{right.name} up"
|
|
66
|
+
sudo "ip netns del #{namespaced(left.node.name)}" if left.node.type == :docker
|
|
67
|
+
sudo "ip netns del #{namespaced(right.node.name)}" if right.node.type == :docker
|
|
68
|
+
when :s2n
|
|
69
|
+
case right.node.type
|
|
70
|
+
when :docker
|
|
71
|
+
kokobr(namespaced(left.switch.name), namespaced(right.node.name), right.name)
|
|
72
|
+
when :netns
|
|
73
|
+
kokobr_netns(namespaced(left.switch.name), namespaced(right.node.name), right.name)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def kokobr(bridge, container, ifname)
|
|
79
|
+
mount_docker_netns(container, container)
|
|
80
|
+
sudo "ip link add name #{ifname} type veth peer name #{container}-#{ifname}"
|
|
81
|
+
sudo "ip link set dev #{ifname} netns #{container}"
|
|
82
|
+
sudo "ip link set #{container}-#{ifname} up"
|
|
83
|
+
sudo "ip netns exec #{container} ip link set #{ifname} up"
|
|
84
|
+
sudo "ip netns del #{container}"
|
|
85
|
+
sudo "ovs-vsctl add-port #{bridge} #{container}-#{ifname}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def kokobr_netns(bridge, container, ifname)
|
|
89
|
+
sudo "ip link add name #{ifname} type veth peer name #{container}-#{ifname}"
|
|
90
|
+
sudo "ip link set dev #{ifname} netns #{container}"
|
|
91
|
+
sudo "ip link set #{container}-#{ifname} up"
|
|
92
|
+
sudo "ip netns exec #{container} ip link set #{ifname} up"
|
|
93
|
+
sudo "ovs-vsctl add-port #{bridge} #{container}-#{ifname}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def koko_physnet(container, netif)
|
|
97
|
+
mount_docker_netns(container, container)
|
|
98
|
+
sudo "ip link set dev #{netif} netns #{container}"
|
|
99
|
+
sudo "ip netns exec #{container} ip link set #{netif} up"
|
|
100
|
+
sudo "ip netns del #{container}"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
data/lib/tinet/data.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
require "tinet/node"
|
|
3
|
+
require "tinet/switch"
|
|
4
|
+
|
|
5
|
+
module Tinet
|
|
6
|
+
class Data
|
|
7
|
+
class << self
|
|
8
|
+
# @param yaml_path [String]
|
|
9
|
+
# @return [Tinet::Data]
|
|
10
|
+
def parse(yaml_path)
|
|
11
|
+
yaml = YAML.load_file(yaml_path)
|
|
12
|
+
raise InvalidYAMLError, "Nodes must be array" unless yaml['nodes'].is_a?(Array)
|
|
13
|
+
raise InvalidYAMLError, "Switches must be array" unless yaml['switches'].is_a?(Array)
|
|
14
|
+
|
|
15
|
+
namespace = yaml.fetch('meta', {}).fetch('namespace', nil)
|
|
16
|
+
Tinet.namespace = namespace unless namespace.nil? || namespace.empty?
|
|
17
|
+
|
|
18
|
+
nodes = yaml['nodes'].map { |node| Tinet::Node.parse(node) }
|
|
19
|
+
switches = yaml['switches'].map { |switch| Tinet::Switch.parse(switch) }
|
|
20
|
+
options = {
|
|
21
|
+
pre_cmd: fetch(yaml, 'pre_cmd'),
|
|
22
|
+
pre_init: fetch(yaml, 'pre_init'),
|
|
23
|
+
post_init: fetch(yaml, 'post_init'),
|
|
24
|
+
pre_conf: fetch(yaml, 'pre_conf'),
|
|
25
|
+
post_conf: fetch(yaml, 'post_conf'),
|
|
26
|
+
pre_down: fetch(yaml, 'pre_down'),
|
|
27
|
+
post_down: fetch(yaml, 'post_down')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
self.new(nodes, switches, options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# @param yaml [Hash]
|
|
36
|
+
# @param key [String]
|
|
37
|
+
# @return [Array<String>]
|
|
38
|
+
def fetch(yaml, key)
|
|
39
|
+
yaml.fetch(key, {}).fetch('cmds', []).map { |cmd| cmd['cmd'] || cmd }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_reader :nodes, :switches, :options
|
|
44
|
+
|
|
45
|
+
# @param nodes [Array<Tinet::Node>]
|
|
46
|
+
# @param switches [Array<Tinet::Switch>]
|
|
47
|
+
# @param options [Hash{Symbol => Array<String>}]
|
|
48
|
+
def initialize(nodes, switches, options)
|
|
49
|
+
@nodes = nodes
|
|
50
|
+
@switches = switches
|
|
51
|
+
@options = options
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/tinet/link.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Tinet
|
|
2
|
+
class Link
|
|
3
|
+
TYPES = %w(n2n s2n).freeze
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
# @param nodes [Array<Tinet::Node>]
|
|
7
|
+
# @param switches [Array<Tinet::Switch>]
|
|
8
|
+
# @return [Array<Tinet::Link>]
|
|
9
|
+
def link(nodes, switches)
|
|
10
|
+
link_n2n(nodes) + link_s2n(nodes, switches)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @param nodes [Array<Tinet::Node>]
|
|
14
|
+
# @return [Array<Tinet::Link>]
|
|
15
|
+
def link_n2n(nodes)
|
|
16
|
+
list, index = [], {}
|
|
17
|
+
nodes.each do |node|
|
|
18
|
+
node.interfaces.each do |interface|
|
|
19
|
+
if interface.type == :direct
|
|
20
|
+
key = "#{interface.args}-#{node.name}##{interface.name}" # example: C0#net0-C1#net0
|
|
21
|
+
if index.key?(key)
|
|
22
|
+
list << self.new(index[key], interface, :n2n)
|
|
23
|
+
else
|
|
24
|
+
key = "#{node.name}##{interface.name}-#{interface.args}" # example: C1#net0-C0#net0
|
|
25
|
+
index[key] = interface
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
list
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @param nodes [Array<Tinet::Node>]
|
|
34
|
+
# @param switches [Array<Tinet::Switch>]
|
|
35
|
+
# @return [Array<Tinet::Link>]
|
|
36
|
+
def link_s2n(nodes, switches)
|
|
37
|
+
list, index = [], {}
|
|
38
|
+
nodes.each do |node|
|
|
39
|
+
node.interfaces.each do |interface|
|
|
40
|
+
key = "#{node.name}-#{interface.args}" # example: C0-B0
|
|
41
|
+
index[key] = interface if interface.type == :bridge
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
switches.each do |switch|
|
|
45
|
+
switch.interfaces.each do |interface|
|
|
46
|
+
key = "#{interface.args}-#{switch.name}" # example: C0-B0
|
|
47
|
+
list << self.new(interface, index[key], :s2n) if index.key?(key)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
list
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_reader :left, :right, :type
|
|
55
|
+
|
|
56
|
+
# @param left [Tinet::Node::Interfase, Tinet::Switch::Interfase]
|
|
57
|
+
# @param right [Tinet::Node::Interfase]
|
|
58
|
+
# @param type [Symbol]
|
|
59
|
+
def initialize(left, right, type)
|
|
60
|
+
@left = left
|
|
61
|
+
@right = right
|
|
62
|
+
@type = type
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/tinet/node.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Tinet
|
|
2
|
+
class Node
|
|
3
|
+
TYPES = %w(docker netns).freeze
|
|
4
|
+
|
|
5
|
+
# @param node_hash [Hash]
|
|
6
|
+
# @return [Tinet::Node]
|
|
7
|
+
def self.parse(node_hash)
|
|
8
|
+
name, type, interfaces = node_hash['name'], node_hash['type'], node_hash['interfaces']
|
|
9
|
+
raise InvalidYAMLError, "Node name is missing" if name.nil? || name.empty?
|
|
10
|
+
raise InvalidTypeError, "Unknown node type: #{type}" unless TYPES.include?(type)
|
|
11
|
+
raise InvalidYAMLError, "Node interfaces must be array" unless interfaces.is_a?(Array)
|
|
12
|
+
|
|
13
|
+
interfaces = interfaces.map { |interface| Interfase.parse(interface) }
|
|
14
|
+
cmds = node_hash.fetch('cmds', []).map { |cmd| cmd['cmd'] || cmd }
|
|
15
|
+
self.new(name, type.to_sym, node_hash['image'], node_hash['build'], interfaces, cmds)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :name, :type, :image, :build, :interfaces, :cmds
|
|
19
|
+
|
|
20
|
+
# @param name [String]
|
|
21
|
+
# @param type [Symbol]
|
|
22
|
+
# @param image [String, nil]
|
|
23
|
+
# @param build [String, nil]
|
|
24
|
+
# @param interfaces [Array<Tinet::Node::Interfase>]
|
|
25
|
+
# @param cmds [Array<String>]
|
|
26
|
+
def initialize(name, type, image, build, interfaces, cmds)
|
|
27
|
+
@name = name
|
|
28
|
+
@type = type
|
|
29
|
+
@image = image
|
|
30
|
+
@build = build
|
|
31
|
+
@interfaces = interfaces
|
|
32
|
+
@cmds = cmds
|
|
33
|
+
interfaces.each { |interface| interface.node = self }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Interfase
|
|
37
|
+
TYPES = %w(direct bridge veth phys).freeze
|
|
38
|
+
|
|
39
|
+
# @param interface_hash [Hash]
|
|
40
|
+
# @return [Tinet::Node::Interfase]
|
|
41
|
+
def self.parse(interface_hash)
|
|
42
|
+
name, type, args = interface_hash['name'], interface_hash['type'], interface_hash['args']
|
|
43
|
+
raise InvalidYAMLError, "Interfase name is missing" if name.nil? || name.empty?
|
|
44
|
+
raise InvalidTypeError, "Unknown interface type: #{type}" unless TYPES.include?(type)
|
|
45
|
+
self.new(name, type.to_sym, args)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
attr_reader :name, :type, :args, :node
|
|
49
|
+
|
|
50
|
+
# @param name [String]
|
|
51
|
+
# @param type [Symbol]
|
|
52
|
+
# @param args [String, nil]
|
|
53
|
+
def initialize(name, type, args)
|
|
54
|
+
@name = name
|
|
55
|
+
@type = type.to_sym
|
|
56
|
+
@args = args
|
|
57
|
+
@node = nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def node=(node)
|
|
61
|
+
@node = node
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require "logger"
|
|
2
|
+
|
|
3
|
+
module Tinet
|
|
4
|
+
class Setting
|
|
5
|
+
ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
|
6
|
+
DEFAULT_SPECFILE_PATH = './spec.yml'.freeze
|
|
7
|
+
|
|
8
|
+
# @return [String]
|
|
9
|
+
def root
|
|
10
|
+
ROOT
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [String]
|
|
14
|
+
def namespace
|
|
15
|
+
@namespace ||= 'tinet'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param namespace [String]
|
|
19
|
+
# @return [String]
|
|
20
|
+
def namespace=(namespace)
|
|
21
|
+
@namespace = namespace
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Logger]
|
|
25
|
+
def logger
|
|
26
|
+
@logger ||= Logger.new($stderr).tap do |logger|
|
|
27
|
+
logger.formatter = proc { |_sev, _dtm, _name, message| message + "\n" }
|
|
28
|
+
logger.level = Logger::INFO
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param logger [Logger]
|
|
33
|
+
# @return [Logger]
|
|
34
|
+
def logger=(logger)
|
|
35
|
+
@logger = logger
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
SettingSingleton = Setting.new
|
|
40
|
+
|
|
41
|
+
class << self
|
|
42
|
+
def const_missing(name)
|
|
43
|
+
Setting.const_get name
|
|
44
|
+
rescue NameError
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def method_missing(method, *args)
|
|
49
|
+
SettingSingleton.__send__ method, *args
|
|
50
|
+
rescue NoMethodError
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/tinet/shell.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "open3"
|
|
2
|
+
require "tinet/setting"
|
|
3
|
+
|
|
4
|
+
module Tinet
|
|
5
|
+
module Shell
|
|
6
|
+
DummyStatus = Struct.new(:success) do |status|
|
|
7
|
+
def success?
|
|
8
|
+
success
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def sudo(command)
|
|
13
|
+
sh "sudo #{command}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sh(command, dry_run: false, print: false, continue: false)
|
|
17
|
+
if dry_run || print
|
|
18
|
+
Tinet.logger.info command
|
|
19
|
+
else
|
|
20
|
+
Tinet.logger.debug command
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return ['', '', DummyStatus.new(true)] if dry_run
|
|
24
|
+
|
|
25
|
+
stdout, stderr, status = Open3.capture3(command)
|
|
26
|
+
|
|
27
|
+
if !status.success? && !continue
|
|
28
|
+
Tinet.logger.error "Command '#{command}' failed:"
|
|
29
|
+
Tinet.logger.error " #{stderr.chomp}" unless stderr.chomp.empty?
|
|
30
|
+
exit(status.to_i)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
[stdout.chomp, stderr.chomp, status]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/tinet/switch.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Tinet
|
|
2
|
+
class Switch
|
|
3
|
+
# @param switch_hash [Hash]
|
|
4
|
+
# @return [Tinet::Switch]
|
|
5
|
+
def self.parse(switch_hash)
|
|
6
|
+
name, interfaces = switch_hash['name'], switch_hash['interfaces']
|
|
7
|
+
raise InvalidYAMLError, "Switch name is missing" if name.nil? || name.empty?
|
|
8
|
+
raise InvalidYAMLError, "Switch interfaces must be array" unless interfaces.is_a?(Array)
|
|
9
|
+
|
|
10
|
+
interfaces = interfaces.map { |interface| Interfase.parse(interface) }
|
|
11
|
+
self.new(name, interfaces)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
attr_reader :name, :interfaces
|
|
15
|
+
|
|
16
|
+
# @param name [String]
|
|
17
|
+
# @param interfaces [Array<Tinet::Switch::Interfase>]
|
|
18
|
+
def initialize(name, interfaces)
|
|
19
|
+
@name = name
|
|
20
|
+
@interfaces = interfaces
|
|
21
|
+
interfaces.each { |interface| interface.switch = self }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Interfase
|
|
25
|
+
TYPES = %w(docker netns phys).freeze
|
|
26
|
+
|
|
27
|
+
# @param interface_hash [Hash]
|
|
28
|
+
# @return [Tinet::Switch::Interfase]
|
|
29
|
+
def self.parse(interface_hash)
|
|
30
|
+
name, type, args = interface_hash['name'], interface_hash['type'], interface_hash['args']
|
|
31
|
+
raise InvalidYAMLError, "Interfase name is missing" if name.nil? || name.empty?
|
|
32
|
+
raise InvalidTypeError, "Unknown interface type: #{type}" unless TYPES.include?(type)
|
|
33
|
+
self.new(name, type.to_sym, args)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_reader :name, :type, :args, :switch
|
|
37
|
+
|
|
38
|
+
# @param name [String]
|
|
39
|
+
# @param type [Symbol]
|
|
40
|
+
# @param args [String, nil]
|
|
41
|
+
def initialize(name, type, args)
|
|
42
|
+
@name = name
|
|
43
|
+
@type = type.to_sym
|
|
44
|
+
@args = args
|
|
45
|
+
@switch = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def switch=(switch)
|
|
49
|
+
@switch = switch
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/spec.template.yml
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
nodes:
|
|
2
|
+
- name: C0
|
|
3
|
+
type: docker # optional (default: docker)
|
|
4
|
+
image: slankdev/ubuntu:18.04
|
|
5
|
+
interfaces:
|
|
6
|
+
- { name: net0, type: direct, args: C1#net0 }
|
|
7
|
+
- { name: net1, type: bridge, args: B0 }
|
|
8
|
+
- { name: net2, type: veth , args: peer0 }
|
|
9
|
+
- { name: net3, type: phys }
|
|
10
|
+
cmds:
|
|
11
|
+
- cmd: ip link set dev net0 up
|
|
12
|
+
|
|
13
|
+
- name: C1
|
|
14
|
+
type: netns # optional (default: docker)
|
|
15
|
+
interfaces:
|
|
16
|
+
- { name: net0, type: direct, args: C0#net0 }
|
|
17
|
+
- { name: net1, type: bridge, args: B0 }
|
|
18
|
+
cmds:
|
|
19
|
+
- cmd: echo slankdev slankdev
|
|
20
|
+
- cmd: >-
|
|
21
|
+
echo slankdev &&
|
|
22
|
+
echo slnakdev
|
|
23
|
+
|
|
24
|
+
switches:
|
|
25
|
+
- name: B0
|
|
26
|
+
interfaces:
|
|
27
|
+
- { name: net0, type: docker, args: C0 }
|
|
28
|
+
- { name: net0, type: netns, args: C1 }
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
- name: p2p
|
|
32
|
+
cmds:
|
|
33
|
+
- cmd: docker exec C0 ping -c2 10.0.0.2
|
|
34
|
+
- cmd: echo slankdev slankdev
|
|
35
|
+
|
|
36
|
+
- name: lo
|
|
37
|
+
cmds:
|
|
38
|
+
- cmd: docker exec C0 ping -c2 10.255.0.1
|
|
39
|
+
- cmd: echo slankdev slankdev
|
data/tinet.gemspec
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "tinet/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "tinet"
|
|
7
|
+
spec.version = Tinet::VERSION
|
|
8
|
+
spec.authors = ["koki-sato"]
|
|
9
|
+
spec.email = ["admin@koki-sato.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby implement of slankdev/tinet."
|
|
12
|
+
spec.homepage = "https://github.com/koki-sato/tinet-rb"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
|
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/koki-sato/tinet-rb"
|
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/koki-sato/tinet-rb/blob/master/CHANGELOG.md"
|
|
18
|
+
|
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|sample|features)/}) }
|
|
23
|
+
end
|
|
24
|
+
spec.bindir = "bin"
|
|
25
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
26
|
+
spec.require_paths = ["lib"]
|
|
27
|
+
|
|
28
|
+
spec.add_dependency "thor"
|
|
29
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
31
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
32
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: tinet
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- koki-sato
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-10-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: thor
|
|
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: bundler
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '10.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '10.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.0'
|
|
69
|
+
description:
|
|
70
|
+
email:
|
|
71
|
+
- admin@koki-sato.com
|
|
72
|
+
executables:
|
|
73
|
+
- tinet
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- ".gitignore"
|
|
78
|
+
- ".rspec"
|
|
79
|
+
- CHANGELOG.md
|
|
80
|
+
- Gemfile
|
|
81
|
+
- LICENSE
|
|
82
|
+
- README.md
|
|
83
|
+
- Rakefile
|
|
84
|
+
- bin/tinet
|
|
85
|
+
- lib/tinet.rb
|
|
86
|
+
- lib/tinet/cli.rb
|
|
87
|
+
- lib/tinet/command/base.rb
|
|
88
|
+
- lib/tinet/command/build.rb
|
|
89
|
+
- lib/tinet/command/conf.rb
|
|
90
|
+
- lib/tinet/command/down.rb
|
|
91
|
+
- lib/tinet/command/exec.rb
|
|
92
|
+
- lib/tinet/command/init.rb
|
|
93
|
+
- lib/tinet/command/ps.rb
|
|
94
|
+
- lib/tinet/command/pull.rb
|
|
95
|
+
- lib/tinet/command/up.rb
|
|
96
|
+
- lib/tinet/data.rb
|
|
97
|
+
- lib/tinet/link.rb
|
|
98
|
+
- lib/tinet/node.rb
|
|
99
|
+
- lib/tinet/setting.rb
|
|
100
|
+
- lib/tinet/shell.rb
|
|
101
|
+
- lib/tinet/switch.rb
|
|
102
|
+
- lib/tinet/version.rb
|
|
103
|
+
- spec.template.yml
|
|
104
|
+
- tinet.gemspec
|
|
105
|
+
homepage: https://github.com/koki-sato/tinet-rb
|
|
106
|
+
licenses:
|
|
107
|
+
- MIT
|
|
108
|
+
metadata:
|
|
109
|
+
homepage_uri: https://github.com/koki-sato/tinet-rb
|
|
110
|
+
source_code_uri: https://github.com/koki-sato/tinet-rb
|
|
111
|
+
changelog_uri: https://github.com/koki-sato/tinet-rb/blob/master/CHANGELOG.md
|
|
112
|
+
post_install_message:
|
|
113
|
+
rdoc_options: []
|
|
114
|
+
require_paths:
|
|
115
|
+
- lib
|
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - ">="
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
requirements: []
|
|
127
|
+
rubyforge_project:
|
|
128
|
+
rubygems_version: 2.7.6
|
|
129
|
+
signing_key:
|
|
130
|
+
specification_version: 4
|
|
131
|
+
summary: Ruby implement of slankdev/tinet.
|
|
132
|
+
test_files: []
|