dewiring 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|