dewiring 0.1.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 +15 -0
- data/bin/wire +7 -0
- data/bin/wire-network-container.sh +547 -0
- data/lib/test_fig.rb +46 -0
- data/lib/wire/cli/cli_commands.rb +88 -0
- data/lib/wire/cli/main_cli.rb +129 -0
- data/lib/wire/cli.rb +8 -0
- data/lib/wire/commands/base_command.rb +139 -0
- data/lib/wire/commands/down_command.rb +69 -0
- data/lib/wire/commands/down_command_handler.rb +199 -0
- data/lib/wire/commands/init_command.rb +89 -0
- data/lib/wire/commands/init_interactive.rb +75 -0
- data/lib/wire/commands/spec_command.rb +240 -0
- data/lib/wire/commands/spec_templates.rb +134 -0
- data/lib/wire/commands/up_command.rb +69 -0
- data/lib/wire/commands/up_command_handler.rb +193 -0
- data/lib/wire/commands/updown_command_base.rb +80 -0
- data/lib/wire/commands/validate_command.rb +64 -0
- data/lib/wire/commands/verify_command.rb +196 -0
- data/lib/wire/commands/verify_command_handler.rb +134 -0
- data/lib/wire/commands.rb +19 -0
- data/lib/wire/common.rb +42 -0
- data/lib/wire/execution/local_exec.rb +110 -0
- data/lib/wire/execution.rb +7 -0
- data/lib/wire/model/appgroup_validation.rb +45 -0
- data/lib/wire/model/loader.rb +49 -0
- data/lib/wire/model/network_validation.rb +111 -0
- data/lib/wire/model/project.rb +64 -0
- data/lib/wire/model/state.rb +154 -0
- data/lib/wire/model/validation.rb +66 -0
- data/lib/wire/model/verification.rb +37 -0
- data/lib/wire/model.rb +13 -0
- data/lib/wire/resource/bridge.rb +76 -0
- data/lib/wire/resource/dhcp_range_config.rb +135 -0
- data/lib/wire/resource/fig_adapter.rb +127 -0
- data/lib/wire/resource/ip_binary.rb +141 -0
- data/lib/wire/resource/ipaddr_ext.rb +38 -0
- data/lib/wire/resource/ipaddr_on_intf.rb +108 -0
- data/lib/wire/resource/network_injection.rb +138 -0
- data/lib/wire/resource/resource.rb +52 -0
- data/lib/wire/resource.rb +14 -0
- data/lib/wire/version.rb +14 -0
- data/lib/wire.rb +24 -0
- metadata +117 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
# Wire module
|
8
|
+
module Wire
|
9
|
+
# ProjectYamlLoader is able to load a model
|
10
|
+
# from yaml files (as written by init command)
|
11
|
+
class ProjectYamlLoader
|
12
|
+
# loads project model from target_dir
|
13
|
+
def load_project(target_dir)
|
14
|
+
# ensure target dir exists, is a dir
|
15
|
+
fail(ArgumentError, 'Nonexisting directory') unless File.exist?(target_dir) &&
|
16
|
+
File.directory?(target_dir)
|
17
|
+
|
18
|
+
# create project
|
19
|
+
project = Project.new(target_dir)
|
20
|
+
|
21
|
+
# iterate all model element types, load if file exists
|
22
|
+
MODEL_ELEMENTS.each do |model_element|
|
23
|
+
filename = File.join(target_dir, "#{model_element}.yaml")
|
24
|
+
|
25
|
+
# jump out unless file exists
|
26
|
+
next unless File.exist?(filename) && File.readable?(filename)
|
27
|
+
|
28
|
+
$log.debug "Loading model file #{filename}"
|
29
|
+
|
30
|
+
element_data = load_model_element_file(filename)
|
31
|
+
project.merge_element model_element, element_data
|
32
|
+
end
|
33
|
+
|
34
|
+
# dump some statistics
|
35
|
+
puts(project.calc_stats.reduce([]) do |res, elem|
|
36
|
+
type = elem[0]
|
37
|
+
count = elem[1]
|
38
|
+
res << "#{count} #{type}(s)"
|
39
|
+
end.join(', '))
|
40
|
+
|
41
|
+
project
|
42
|
+
end
|
43
|
+
|
44
|
+
# reads filename as yaml, returns elements
|
45
|
+
def load_model_element_file(filename)
|
46
|
+
YAML.load(File.open(filename, 'r'))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
# Wire module
|
8
|
+
module Wire
|
9
|
+
# Run validations on network model part
|
10
|
+
class NetworksValidation < ValidationBase
|
11
|
+
# run validation steps on network elements
|
12
|
+
# returns:
|
13
|
+
# - nil, results in errors of ValidationBase
|
14
|
+
def run_validations
|
15
|
+
networks_attached_to_zones?
|
16
|
+
duplicate_networks_found?
|
17
|
+
missing_network_def_found?
|
18
|
+
nonmatching_hostips_found?
|
19
|
+
dhcp_address_ranges_valid?
|
20
|
+
end
|
21
|
+
|
22
|
+
# ensures that all networks are attached to a zone
|
23
|
+
def networks_attached_to_zones?
|
24
|
+
objects_attached_to_zones? 'networks'
|
25
|
+
end
|
26
|
+
|
27
|
+
# ensures that all network ranges are unique
|
28
|
+
def duplicate_networks_found?
|
29
|
+
dup_map = {}
|
30
|
+
@project.get_element('networks').each do |network_name, network_data|
|
31
|
+
nw = network_data[:network]
|
32
|
+
dupe_name = dup_map[nw]
|
33
|
+
|
34
|
+
mark("Network range #{nw} used in more than one network (#{dupe_name})",
|
35
|
+
'network', network_name) if dupe_name
|
36
|
+
dup_map.store nw, network_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# ensures that all networks have their network range defined
|
41
|
+
def missing_network_def_found?
|
42
|
+
@project.get_element('networks').each do |network_name, network_data|
|
43
|
+
nw = network_data[:network]
|
44
|
+
mark("Network #{network_name} has no network ip range.",
|
45
|
+
'network', network_name) unless nw
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# ensures that all networks with hostips have their hostip
|
50
|
+
# within the network range of its network, i.e. 10.10.1.1 for 10.10.1.0/24
|
51
|
+
def nonmatching_hostips_found?
|
52
|
+
@project.get_element('networks').each do |network_name, network_data|
|
53
|
+
network = network_data[:network]
|
54
|
+
host_ip = network_data[:hostip]
|
55
|
+
next unless network && host_ip
|
56
|
+
|
57
|
+
host_ip_ip = IPAddr.new(host_ip)
|
58
|
+
network_ip = IPAddr.new(network)
|
59
|
+
|
60
|
+
mark("Network Host ip #{host_ip} is not within network range" \
|
61
|
+
"#{network} of network #{network_name}", 'network', network_name) unless
|
62
|
+
host_ip_ip.in_range_of?(network_ip)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# ensures that if a network has dhcp set, its :start/:end address
|
67
|
+
# ranges are within the address range of network, and a hostip is
|
68
|
+
# given (for dnsmasq to udp-listen on it)
|
69
|
+
# rubocop:disable CyclomaticComplexity
|
70
|
+
def dhcp_address_ranges_valid?
|
71
|
+
@project.get_element('networks').each do |network_name, network_data|
|
72
|
+
network = network_data[:network]
|
73
|
+
dhcp_data = network_data[:dhcp]
|
74
|
+
next unless network && dhcp_data
|
75
|
+
|
76
|
+
# do we have a host-ip on this bridge?
|
77
|
+
host_ip = network_data[:hostip]
|
78
|
+
if !host_ip
|
79
|
+
mark("Network #{network_name} wants dhcp, but does not include a hostip.",
|
80
|
+
'network', network_name)
|
81
|
+
return false
|
82
|
+
else
|
83
|
+
if !dhcp_data[:start] || !dhcp_data[:end]
|
84
|
+
mark("Network #{network_name} wants dhcp, but does not include an " \
|
85
|
+
'address range. Set :start, :end.',
|
86
|
+
'network', network_name)
|
87
|
+
return false
|
88
|
+
else
|
89
|
+
# check ip ranges
|
90
|
+
|
91
|
+
begin
|
92
|
+
dhcp_start_ip = IPAddr.new(dhcp_data[:start])
|
93
|
+
dhcp_end_ip = IPAddr.new(dhcp_data[:end])
|
94
|
+
network_ip = IPAddr.new(network)
|
95
|
+
|
96
|
+
mark("Network dhcp start ip #{dhcp_data[:start]} is not within network range" \
|
97
|
+
"#{network} of network #{network_name}", 'network', network_name) unless
|
98
|
+
dhcp_start_ip.in_range_of?(network_ip)
|
99
|
+
|
100
|
+
mark("Network dhcp end ip #{dhcp_data[:end]} is not within network range" \
|
101
|
+
"#{network} of network #{network_name}", 'network', network_name) unless
|
102
|
+
dhcp_end_ip.in_range_of?(network_ip)
|
103
|
+
rescue => e
|
104
|
+
mark("Network dhcp ip range is not valid: #{e}", 'network', network_name)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
# Wire module
|
8
|
+
module Wire
|
9
|
+
# define model elements for lookup purposes
|
10
|
+
MODEL_ELEMENTS = %w( zones networks appgroups )
|
11
|
+
|
12
|
+
# Defines a project with model elements
|
13
|
+
# (zones, networks, ...) as open structs
|
14
|
+
class Project
|
15
|
+
# +target_dir+ is the directory where model files are stores
|
16
|
+
# +data+ is a [Hash] of all model objects
|
17
|
+
attr_accessor :target_dir, :data
|
18
|
+
|
19
|
+
# set up empty project. Sets +target_dir+
|
20
|
+
def initialize(target_dir)
|
21
|
+
@target_dir = target_dir
|
22
|
+
@data = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# merge in hash data
|
26
|
+
# Params:
|
27
|
+
# +element_name+ Name of model element part, i.e. 'zones'
|
28
|
+
# +element_data+ [Hash] of model element data
|
29
|
+
def merge_element(element_name, element_data)
|
30
|
+
@data.merge!({ element_name.to_sym => element_data })
|
31
|
+
end
|
32
|
+
|
33
|
+
# check if we have a model element (i.e. zones)
|
34
|
+
# params:
|
35
|
+
# +element_name+ Name of model element part, i.e. 'backend-zone'
|
36
|
+
def element?(element_name)
|
37
|
+
@data.key? element_name.to_sym
|
38
|
+
end
|
39
|
+
|
40
|
+
# retrieve element hash, raise ArgumentError if
|
41
|
+
# it does not exist.
|
42
|
+
# params:
|
43
|
+
# +element_name+ Name of model element part, i.e. 'backend-zone'
|
44
|
+
def get_element(element_name)
|
45
|
+
fail(
|
46
|
+
ArgumentError, "Element #{element_name} not found"
|
47
|
+
) unless element?(element_name)
|
48
|
+
@data[element_name.to_sym]
|
49
|
+
end
|
50
|
+
|
51
|
+
# calculates count statistics on project
|
52
|
+
# returns:
|
53
|
+
# - [Hash], key => element type, value => [int] count
|
54
|
+
def calc_stats
|
55
|
+
result = {}
|
56
|
+
|
57
|
+
@data.each do |element_name, element_data|
|
58
|
+
result[element_name] = element_data.size
|
59
|
+
end
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'singleton'
|
8
|
+
|
9
|
+
# Wire module
|
10
|
+
module Wire
|
11
|
+
# +State+ is a container for a state model
|
12
|
+
# :reek:DataClump
|
13
|
+
class State
|
14
|
+
# [Hash] of all +state+ entries, key=name, value=StateEntry
|
15
|
+
attr_accessor :state
|
16
|
+
# backref to project
|
17
|
+
attr_accessor :project
|
18
|
+
# did something change? since last change or load/save
|
19
|
+
attr_reader :changed
|
20
|
+
|
21
|
+
include Singleton
|
22
|
+
|
23
|
+
# creates an empty state
|
24
|
+
def initialize
|
25
|
+
clean
|
26
|
+
end
|
27
|
+
|
28
|
+
# cleans the state
|
29
|
+
def clean
|
30
|
+
@state = {}
|
31
|
+
# +changed+ indicates wether state has changed
|
32
|
+
@changed = false
|
33
|
+
end
|
34
|
+
|
35
|
+
# adds or updates state
|
36
|
+
# +type+ i.e. :bridge
|
37
|
+
# +name+ i.e. :br0
|
38
|
+
# +state+, one of :up, :down, :unknown
|
39
|
+
def update(type, name, state)
|
40
|
+
key = (make_key(type, name))
|
41
|
+
entry = @state[key]
|
42
|
+
if entry
|
43
|
+
(entry.state != state) && @changed = true
|
44
|
+
entry.state = state
|
45
|
+
else
|
46
|
+
@state.store(key, StateEntry.new(type, name, state))
|
47
|
+
@changed = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# combines +type+ and +name+ into a key suitable for the hash
|
52
|
+
def make_key(type, name)
|
53
|
+
"%#{type}%#{name}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# checks if we have a state for resource
|
57
|
+
# given by +type+ and +name+
|
58
|
+
def state?(type, name)
|
59
|
+
@state.key?(make_key(type, name))
|
60
|
+
end
|
61
|
+
|
62
|
+
# checks if +type+ resource +name+
|
63
|
+
# is in state +state_to_check+
|
64
|
+
def check(type, name, state_to_check)
|
65
|
+
key = (make_key(type, name))
|
66
|
+
entry = @state[key]
|
67
|
+
return entry.state == state_to_check if entry
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
# checks if resource +type+ +name+ is up
|
72
|
+
def up?(type, name)
|
73
|
+
check(type, name, :up)
|
74
|
+
end
|
75
|
+
|
76
|
+
# checks if resource +type+ +name+ is down
|
77
|
+
def down?(type, name)
|
78
|
+
check(type, name, :down)
|
79
|
+
end
|
80
|
+
|
81
|
+
# returns changed flad
|
82
|
+
def changed?
|
83
|
+
changed
|
84
|
+
end
|
85
|
+
|
86
|
+
# calls to_pretty_s on a state entries
|
87
|
+
def to_pretty_s
|
88
|
+
@state.reduce([]) do |arr, entry|
|
89
|
+
arr << entry[1].to_pretty_s
|
90
|
+
end.join(',')
|
91
|
+
end
|
92
|
+
|
93
|
+
# save current state to statefile (within project target dir)
|
94
|
+
def save
|
95
|
+
unless @changed
|
96
|
+
$log.debug 'Not saving state, nothing changed'
|
97
|
+
return
|
98
|
+
end
|
99
|
+
statefile_filename = state_filename
|
100
|
+
$log.debug "Saving state to #{statefile_filename}"
|
101
|
+
File.open(statefile_filename, 'w') do |file|
|
102
|
+
file.puts state.to_yaml
|
103
|
+
end
|
104
|
+
@changed = false
|
105
|
+
end
|
106
|
+
|
107
|
+
# load state from statefile (within project target dir)
|
108
|
+
def load
|
109
|
+
statefile_filename = state_filename
|
110
|
+
if File.exist?(statefile_filename) &&
|
111
|
+
File.file?(statefile_filename) &&
|
112
|
+
File.readable?(statefile_filename)
|
113
|
+
$log.debug "Loading state from #{statefile_filename}"
|
114
|
+
@state = YAML.load_file(statefile_filename)
|
115
|
+
else
|
116
|
+
$log.debug 'No statefile found.'
|
117
|
+
end
|
118
|
+
@changed = false
|
119
|
+
end
|
120
|
+
|
121
|
+
# construct name of state file
|
122
|
+
def state_filename
|
123
|
+
File.join(@project.target_dir, '.state.yaml')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# A StateEntry combines a resource type,
|
128
|
+
# a resource and the state
|
129
|
+
class StateEntry
|
130
|
+
# +type+ i.e. :bridge
|
131
|
+
# +name+ i.e. :br0
|
132
|
+
# +state+, one of :up, :down, :unknown
|
133
|
+
attr_accessor :type, :name, :state
|
134
|
+
|
135
|
+
# initializes the state entry
|
136
|
+
# with given +type+ and +name+ and +state+
|
137
|
+
# sets :unknown state if +state+ not given
|
138
|
+
def initialize(type, name, state = :unknown)
|
139
|
+
self.type = type
|
140
|
+
self.name = name
|
141
|
+
self.state = state
|
142
|
+
end
|
143
|
+
|
144
|
+
# string representation
|
145
|
+
def to_s
|
146
|
+
"State:[type=#{type}, name=#{name}, state=#{state}]"
|
147
|
+
end
|
148
|
+
|
149
|
+
# readble string repr for output
|
150
|
+
def to_pretty_s
|
151
|
+
"#{type}:#{name} is #{state}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
# Wire module
|
8
|
+
module Wire
|
9
|
+
# raised in case of invalid model
|
10
|
+
class ValidationError
|
11
|
+
# +message+:: Validation Error message
|
12
|
+
# +element_type+:: Model element type of this error, i.e. 'Network'
|
13
|
+
# +element_name+:: Model element name
|
14
|
+
attr_accessor :message, :element_type, :element_name
|
15
|
+
|
16
|
+
# Initializes the error object
|
17
|
+
# Params:
|
18
|
+
# +message+:: Validation Error message
|
19
|
+
# +element_type+:: Model element type of this error, i.e. 'Network'
|
20
|
+
# +element_name+:: Model element name
|
21
|
+
def initialize(message, element_type, element_name)
|
22
|
+
@message = message
|
23
|
+
@element_type = element_type
|
24
|
+
@element_name = element_name
|
25
|
+
end
|
26
|
+
|
27
|
+
# returns a string representation
|
28
|
+
def to_s
|
29
|
+
"ValidationError on #{@element_type} #{@element_name}: #{@message}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Validation Base class
|
34
|
+
class ValidationBase
|
35
|
+
# +errors+ Array of validation errors, see class ValidationError
|
36
|
+
attr_accessor :errors
|
37
|
+
|
38
|
+
# initializes the Validation object on given +project+
|
39
|
+
def initialize(project)
|
40
|
+
@project = project
|
41
|
+
@errors = []
|
42
|
+
end
|
43
|
+
|
44
|
+
# adds a validation error to the error list
|
45
|
+
# +message+ Validation Error message
|
46
|
+
# +element_type+ Model element type of this error, i.e. 'Network'
|
47
|
+
# +element_name+ Model element name
|
48
|
+
def mark(message, element_type, element_name)
|
49
|
+
@errors << ValidationError.new(message, element_type, element_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# ensures that objects of given +type_as_string+ (i.e. networks)
|
53
|
+
# are attached to zones
|
54
|
+
def objects_attached_to_zones?(type_as_string)
|
55
|
+
zones = @project.get_element('zones')
|
56
|
+
@project.get_element(type_as_string).each do |name, data|
|
57
|
+
zone = data[:zone] # assume that this object contains ref to a zone
|
58
|
+
if !zone
|
59
|
+
mark("#{type_as_string} is not attached to a zone", type_as_string, name)
|
60
|
+
else
|
61
|
+
mark("#{type_as_string} has invalid zone", type_as_string, name) unless zones.key?(zone)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
# Wire module
|
8
|
+
module Wire
|
9
|
+
# raised in case of model elements
|
10
|
+
# not running or in other verification
|
11
|
+
# states
|
12
|
+
class VerificationError
|
13
|
+
# +message+:: verification (error) message
|
14
|
+
# +element_type+:: element type as string (i.e. 'network')
|
15
|
+
# +element_name+:: name of element within model
|
16
|
+
# +element+:: reference to model element
|
17
|
+
attr_accessor :message, :element_type, :element_name, :element
|
18
|
+
|
19
|
+
# initalizes the verification error
|
20
|
+
# params:
|
21
|
+
# +message+:: verification (error) message
|
22
|
+
# +element_type+:: element type as string (i.e. 'network')
|
23
|
+
# +element_name+:: name of element within model
|
24
|
+
# +element+:: reference to model element
|
25
|
+
def initialize(message, element_type, element_name, element)
|
26
|
+
@message = message
|
27
|
+
@element_type = element_type
|
28
|
+
@element_name = element_name
|
29
|
+
@element = element
|
30
|
+
end
|
31
|
+
|
32
|
+
# string representation
|
33
|
+
def to_s
|
34
|
+
"VerificationError on #{@element_type} #{@element_name}: #{@message}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/wire/model.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
require_relative 'model/project.rb'
|
8
|
+
require_relative 'model/loader.rb'
|
9
|
+
require_relative 'model/validation.rb'
|
10
|
+
require_relative 'model/network_validation.rb'
|
11
|
+
require_relative 'model/appgroup_validation.rb'
|
12
|
+
require_relative 'model/verification.rb'
|
13
|
+
require_relative 'model/state.rb'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The MIT License (MIT)
|
4
|
+
# Copyright (c) 2014 Andreas Schmidt, andreas@de-wiring.net
|
5
|
+
#
|
6
|
+
|
7
|
+
include Wire::Execution
|
8
|
+
|
9
|
+
# Wire module
|
10
|
+
module Wire
|
11
|
+
# Resource module
|
12
|
+
module Resource
|
13
|
+
# Open vSwitch Bridge resource
|
14
|
+
class OVSBridge < ResourceBase
|
15
|
+
# +type+ of bridge (here: ovs)
|
16
|
+
# +executables+ [Hash] of binaries needed to control
|
17
|
+
# the resource
|
18
|
+
attr_accessor :type, :executables
|
19
|
+
|
20
|
+
# initialize the bridge object with
|
21
|
+
# given +name+ and type
|
22
|
+
# params:
|
23
|
+
# - name bridge name, i.e. "br0"
|
24
|
+
def initialize(name)
|
25
|
+
super(name)
|
26
|
+
|
27
|
+
# TODO: make configurable
|
28
|
+
@executables = {
|
29
|
+
:vsctl => '/usr/bin/ovs-vsctl'
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# checks if the bridge exists
|
34
|
+
def exist?
|
35
|
+
LocalExecution.with(@executables[:vsctl],
|
36
|
+
['br-exists', @name]) do |exec_obj|
|
37
|
+
exec_obj.run
|
38
|
+
return (exec_obj.exitstatus != 2)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# checks if the bridge exists
|
43
|
+
def up?
|
44
|
+
exist?
|
45
|
+
end
|
46
|
+
|
47
|
+
# adds the bridge (ovs-vsctl add-br)
|
48
|
+
def up
|
49
|
+
LocalExecution.with(@executables[:vsctl],
|
50
|
+
['add-br', @name]) do |up_exec_obj|
|
51
|
+
up_exec_obj.run
|
52
|
+
return (up_exec_obj.exitstatus == 0)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# checks if the bridge is down
|
57
|
+
def down?
|
58
|
+
!(up?)
|
59
|
+
end
|
60
|
+
|
61
|
+
# deletes the bridge (ovs-vsctl del-br)
|
62
|
+
def down
|
63
|
+
LocalExecution.with(@executables[:vsctl],
|
64
|
+
['del-br', @name]) do |down_exec_obj|
|
65
|
+
down_exec_obj.run
|
66
|
+
return (down_exec_obj.exitstatus == 0)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a string representation
|
71
|
+
def to_s
|
72
|
+
"Bridge:[#{name},type=#{type}]"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|