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.
Files changed (44) hide show
  1. checksums.yaml +15 -0
  2. data/bin/wire +7 -0
  3. data/bin/wire-network-container.sh +547 -0
  4. data/lib/test_fig.rb +46 -0
  5. data/lib/wire/cli/cli_commands.rb +88 -0
  6. data/lib/wire/cli/main_cli.rb +129 -0
  7. data/lib/wire/cli.rb +8 -0
  8. data/lib/wire/commands/base_command.rb +139 -0
  9. data/lib/wire/commands/down_command.rb +69 -0
  10. data/lib/wire/commands/down_command_handler.rb +199 -0
  11. data/lib/wire/commands/init_command.rb +89 -0
  12. data/lib/wire/commands/init_interactive.rb +75 -0
  13. data/lib/wire/commands/spec_command.rb +240 -0
  14. data/lib/wire/commands/spec_templates.rb +134 -0
  15. data/lib/wire/commands/up_command.rb +69 -0
  16. data/lib/wire/commands/up_command_handler.rb +193 -0
  17. data/lib/wire/commands/updown_command_base.rb +80 -0
  18. data/lib/wire/commands/validate_command.rb +64 -0
  19. data/lib/wire/commands/verify_command.rb +196 -0
  20. data/lib/wire/commands/verify_command_handler.rb +134 -0
  21. data/lib/wire/commands.rb +19 -0
  22. data/lib/wire/common.rb +42 -0
  23. data/lib/wire/execution/local_exec.rb +110 -0
  24. data/lib/wire/execution.rb +7 -0
  25. data/lib/wire/model/appgroup_validation.rb +45 -0
  26. data/lib/wire/model/loader.rb +49 -0
  27. data/lib/wire/model/network_validation.rb +111 -0
  28. data/lib/wire/model/project.rb +64 -0
  29. data/lib/wire/model/state.rb +154 -0
  30. data/lib/wire/model/validation.rb +66 -0
  31. data/lib/wire/model/verification.rb +37 -0
  32. data/lib/wire/model.rb +13 -0
  33. data/lib/wire/resource/bridge.rb +76 -0
  34. data/lib/wire/resource/dhcp_range_config.rb +135 -0
  35. data/lib/wire/resource/fig_adapter.rb +127 -0
  36. data/lib/wire/resource/ip_binary.rb +141 -0
  37. data/lib/wire/resource/ipaddr_ext.rb +38 -0
  38. data/lib/wire/resource/ipaddr_on_intf.rb +108 -0
  39. data/lib/wire/resource/network_injection.rb +138 -0
  40. data/lib/wire/resource/resource.rb +52 -0
  41. data/lib/wire/resource.rb +14 -0
  42. data/lib/wire/version.rb +14 -0
  43. data/lib/wire.rb +24 -0
  44. 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