dewiring 0.1.2

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