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,129 @@
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
+ # WireCLI
10
+ # thor command line class
11
+ #
12
+ class WireCLI < Thor
13
+ # treat as non-thor commands
14
+ no_commands do
15
+ # pre-build array of available commands
16
+ def initialize_commands
17
+ @wire_commands = Wire::WireCommands.new
18
+ end
19
+
20
+ def apply_globals
21
+ options[:nocolor] && Rainbow.enabled = false
22
+ options[:debug] && $log.level = Logger::DEBUG
23
+ end
24
+
25
+ end
26
+
27
+ # set global options
28
+ class_option :nocolor, { :desc => 'Disable coloring in output',
29
+ :required => false, :banner => '' }
30
+ class_option :debug, { :desc => 'Show debug output', :banner => '' }
31
+
32
+ desc 'init [TARGETDIR]', 'create an inital model in TARGETDIR'
33
+ long_desc <<-LONGDESC
34
+ Creates TARGETDIR if necessary, opens an interactive
35
+ console dialog about the model to be created.
36
+
37
+ Writes model files to TARGETDIR.
38
+ LONGDESC
39
+ # init method, run init in +target_dir+
40
+ def init(target_dir = '.')
41
+ initialize_commands
42
+ apply_globals
43
+ @wire_commands.run_init(target_dir)
44
+ end
45
+
46
+ # validate
47
+ #
48
+ desc 'validate [TARGETDIR]',
49
+ 'read model in TARGETDIR and validate its consistency'
50
+ long_desc <<-LONGDESC
51
+ Given a model in TARGETDIR, the validate commands reads
52
+ the model and runs consistency checks against the model elements,
53
+ i.e. if every network is attached to a zone.
54
+ LONGDESC
55
+ # validate method, run model validation in +target_dir+
56
+ def validate(target_dir = '.')
57
+ initialize_commands
58
+ apply_globals
59
+ @wire_commands.run_validate(target_dir)
60
+ end
61
+
62
+ # verify
63
+ #
64
+ desc 'verify [TARGETDIR]',
65
+ 'read model in TARGETDIR and verify against current system'
66
+ long_desc <<-LONGDESC
67
+ Given a model in TARGETDIR, the verify commands reads
68
+ the model and runs checks to see if everthing in the model
69
+ is present in the current system.
70
+ LONGDESC
71
+ # verify method, run model verification in +target_dir+
72
+ def verify(target_dir = '.')
73
+ initialize_commands
74
+ apply_globals
75
+ @wire_commands.run_verify(target_dir)
76
+ end
77
+
78
+ # spec
79
+ #
80
+ desc 'spec [TARGETDIR]',
81
+ 'read model in TARGETDIR and create a serverspec' \
82
+ 'specification example in TARGETDIR'
83
+ long_desc <<-LONGDESC
84
+ Given a model in TARGETDIR, the verify commands reads
85
+ the model. For each element it creates a serverspec-conformant
86
+ describe()-block in a spec file.
87
+ Writes spec helpers if they do not exist.
88
+ LONGDESC
89
+ option :run, { :desc => 'Automatically run serverspec', :banner => '', :type => :boolean }
90
+ # spec method, generate and optionally run serverspec in +target_dir+
91
+ def spec(target_dir = '.')
92
+ initialize_commands
93
+ apply_globals
94
+ @wire_commands.run_spec(target_dir, options[:run])
95
+ end
96
+
97
+ # up
98
+ #
99
+ desc 'up [TARGETDIR]',
100
+ 'read model in TARGETDIR and bring system up' \
101
+ 'according to model elements in TARGETDIR'
102
+ long_desc <<-LONGDESC
103
+ Given a model in TARGETDIR, the up commands reads
104
+ the model. Each model element is brought to live.
105
+ LONGDESC
106
+ # up method, bring model in +target_dir+ up
107
+ def up(target_dir = '.')
108
+ initialize_commands
109
+ apply_globals
110
+ @wire_commands.run_up(target_dir)
111
+ end
112
+
113
+ # down
114
+ #
115
+ desc 'down [TARGETDIR]',
116
+ 'read model in TARGETDIR and bring system down' \
117
+ 'according to model elements in TARGETDIR'
118
+ long_desc <<-LONGDESC
119
+ Given a model in TARGETDIR, the up commands reads
120
+ the model. Each model element is stopped and removed.
121
+ LONGDESC
122
+ # down method, bring model in +target_dir+ down
123
+ def down(target_dir = '.')
124
+ initialize_commands
125
+ apply_globals
126
+ @wire_commands.run_down(target_dir)
127
+ end
128
+ end
129
+ end
data/lib/wire/cli.rb ADDED
@@ -0,0 +1,8 @@
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 'cli/main_cli.rb'
8
+ require_relative 'cli/cli_commands.rb'
@@ -0,0 +1,139 @@
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
+ # (Empty) Base command
10
+ class BaseCommand
11
+ # +params+ object and +project+ to operate upon
12
+ attr_accessor :params, :project
13
+
14
+ # returns the state object
15
+ def state
16
+ State.instance
17
+ end
18
+
19
+ # debugs a single line state
20
+ def dump_state
21
+ $log.debug "State: [#{state.to_pretty_s}]"
22
+ end
23
+
24
+ # +outputs+ writes a message to stdout, in given style
25
+ #
26
+ # params:
27
+ # +type+ 1st column as type, i.e. "model" or "network" or "OK"
28
+ # +msg+ message to print
29
+ # +style+ coloring etc, supported:
30
+ # :plain (default), :err (red), :ok (green)
31
+ # return
32
+ # - nil
33
+ #
34
+ # :reek:ControlParameter
35
+ def outputs(type, msg, style = :plain)
36
+ line = "#{type}> #{msg}"
37
+ line = line.color(:red) if (style == :err) || (style == :error)
38
+ line = line.color(:green) if style == :ok
39
+ line = line.color(:cyan) if style == :ok2
40
+
41
+ $stdout.puts line
42
+ end
43
+
44
+ # Issues a warning if we do not run as root.
45
+ def check_user
46
+ (ENV['USER'] != 'root') &&
47
+ $log.warn("Not running as root. Make sure user #{ENV['USER']} has sudo configured.")
48
+ end
49
+
50
+ # retrieve all objects of given +type_name+ in
51
+ # zone (by +zone_name+)
52
+ # returns:
53
+ # [Hash] of model subpart with elements of given type
54
+ def objects_in_zone(type_name, zone_name)
55
+ return {} unless @project.element?(type_name)
56
+ objects = @project.get_element type_name || {}
57
+ objects.select { |_, data| data[:zone] == zone_name }
58
+ end
59
+
60
+ # runs the command, according to parameters
61
+ # loads project into @project, calls run_on_project
62
+ # (to be defined in subclasses)
63
+ # params
64
+ # +params+ command parameter map, example key i.e. "target_dir"
65
+ def run(params = {})
66
+ check_user
67
+
68
+ @params = params
69
+ target_dir = @params[:target_dir]
70
+ outputs 'model', "Loading model in #{target_dir}"
71
+
72
+ # load it first
73
+ begin
74
+ loader = ProjectYamlLoader.new
75
+ @project = loader.load_project(target_dir)
76
+
77
+ # try to load state file.
78
+ state.project = @project
79
+ handle_state_load
80
+
81
+ run_on_project
82
+
83
+ $log.debug? && pp(@project)
84
+
85
+ handle_state_save
86
+
87
+ rescue => load_execption
88
+ $stderr.puts "Unable to process project model in #{target_dir}"
89
+ $log.debug? && puts(load_execption.inspect)
90
+ $log.debug? && puts(load_execption.backtrace)
91
+
92
+ return false
93
+ end
94
+ true
95
+ end
96
+
97
+ # if the hostip is not in cidr, take netmask
98
+ # from network entry, add to hostip
99
+ # params:
100
+ # +host_ip+ i.e. 192.168.10.1
101
+ # +network_data+ network data object, to take netmask from :network element
102
+ def ensure_hostip_netmask(host_ip, network_data)
103
+ return host_ip if host_ip =~ /[0-9\.]+\/[0-9]+/
104
+
105
+ match_data = network_data[:network].match(/[0-9\.]+(\/[0-9]+)/)
106
+ if match_data && match_data.size >= 2
107
+ netmask = match_data[1]
108
+ $log.debug "Adding netmask #{netmask} to host-ip #{host_ip}"
109
+ return "#{host_ip}#{netmask}"
110
+ else
111
+ $log.error "host-ip #{host_ip} is missing netmask, and none given in network."
112
+ return host_ip
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ # Save state to state file
119
+ def handle_state_save
120
+ # dump state
121
+ $log.debug? && dump_state
122
+ state.save
123
+ rescue => save_exception
124
+ $stderr.puts "Error saving state, #{save_exception}"
125
+ $log.debug? && puts(save_exception.inspect)
126
+ $log.debug? && puts(save_exception.backtrace)
127
+ end
128
+
129
+ def handle_state_load
130
+ state.load
131
+ # dump state
132
+ $log.debug? && dump_state
133
+ rescue => load_exception
134
+ $stderr.puts "Error loading state, #{load_exception}"
135
+ $log.debug? && puts(load_exception.inspect)
136
+ $log.debug? && puts(load_exception.backtrace)
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,69 @@
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
+ # DownCommand reads yaml, parses model elements
10
+ # and brings all defined model elements "down", that is
11
+ # stopping and removing bridges, containers etc.
12
+ # - :target_dir
13
+ class DownCommand < UpDownCommand
14
+ # allow to get access to handler object
15
+ attr_reader :handler
16
+
17
+ # initializes DownCommand, creates handler
18
+ def initialize
19
+ super
20
+ @handler = DownCommandHandler.new
21
+ end
22
+
23
+ # run in given zone:
24
+ # returns:
25
+ # - bool: true if successful, false otherwise
26
+ def run_on_zone(zone_name)
27
+ b_result = true
28
+
29
+ networks = @project.get_element('networks')
30
+
31
+ # select appgroups in this zone and take them down first
32
+ appgroups_in_zone = objects_in_zone('appgroups', zone_name)
33
+ appgroups_in_zone.each do |appgroup_name, appgroup_data|
34
+ $log.debug("Processing appgroup \'#{appgroup_name}\'")
35
+
36
+ # process network attachments
37
+ zone_networks = objects_in_zone('networks', zone_name)
38
+ success = handler.handle_network_attachments(zone_name, zone_networks,
39
+ appgroup_name, appgroup_data,
40
+ @project.target_dir)
41
+ b_result &= success
42
+
43
+ # then take down containers
44
+ success = handler.handle_appgroup(zone_name,
45
+ appgroup_name, appgroup_data,
46
+ @project.target_dir)
47
+ b_result &= success
48
+
49
+ end
50
+
51
+ # select networks in current zone only
52
+ networks_in_zone = UpDownCommand.get_networks_for_zone(networks, zone_name)
53
+ networks_in_zone.each do |network_name, network_data|
54
+ $log.debug("Bringing down network #{network_name}")
55
+
56
+ # if we have dhcp, unconfigure dnsmasq
57
+ b_result &= default_handle_dhcp(zone_name, network_name, network_data, @handler)
58
+
59
+ # if we have a host ip on that bridge, take it down first
60
+ b_result &= default_handle_hostip(network_name, network_data, @handler)
61
+
62
+ # we should have a bridge with that name.
63
+ success = @handler.handle_bridge(network_name)
64
+ b_result &= success
65
+ end
66
+ b_result
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,199 @@
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
+ # implements handle_xxx methods for DownCommand
10
+ # rubocop:disable ClassLength
11
+ class DownCommandHandler < BaseCommand
12
+ # take bridge down
13
+ def handle_bridge(bridge_name)
14
+ bridge_resource = Wire::Resource::ResourceFactory.instance.create(:ovsbridge, bridge_name)
15
+ if bridge_resource.down?
16
+ outputs 'DOWN', "Bridge #{bridge_name} already down.", :ok2
17
+ return true
18
+ end
19
+
20
+ bridge_resource.down
21
+ if bridge_resource.down?
22
+ outputs 'DOWN', "Bridge #{bridge_name} down/removed.", :ok
23
+ state.update(:bridge, bridge_name, :down)
24
+ else
25
+ outputs 'DOWN', "Error bringing down bridge #{bridge_name}.", :err
26
+ b_result = false
27
+ end
28
+
29
+ b_result
30
+ end
31
+
32
+ # remove ip from bridge interface
33
+ def handle_hostip(bridge_name, hostip)
34
+ b_result = true
35
+
36
+ bridge_resource = Wire::Resource::ResourceFactory.instance.create(:ovsbridge, bridge_name)
37
+ if bridge_resource.down?
38
+ outputs 'DOWN', "Bridge #{bridge_name} already down, will not care about ip", :ok2
39
+ return true
40
+ end
41
+
42
+ # we should have a bridge with that name.
43
+ hostip_resource = Wire::Resource::ResourceFactory
44
+ .instance.create(:bridgeip, hostip, bridge_name)
45
+ if hostip_resource.down?
46
+ outputs 'DOWN', "IP #{hostip} on bridge #{bridge_name} already down.", :ok2
47
+ return
48
+ end
49
+
50
+ hostip_resource.down
51
+ if hostip_resource.down?
52
+ outputs 'DOWN', "IP #{hostip} on bridge #{bridge_name} down/removed.", :ok
53
+ state.update(:hostip, hostip, :down)
54
+ else
55
+ outputs 'DOWN', "Error taking down ip #{hostip} on bridge #{bridge_name}.", :err
56
+
57
+ b_result = false
58
+ end
59
+
60
+ b_result
61
+ end
62
+
63
+ # unconfigures dnsmasq for dhcp
64
+ # params:
65
+ # +zone_name+ name of zone
66
+ # +network_name+ name of network (and bridge)
67
+ # +network+ network entry
68
+ # +address_start+ start of address range (i.e.192.168.10.10)
69
+ # +address_end+ end of dhcp address range (i.e.192.168.10.100)
70
+ # Returns
71
+ # - [Bool] true if dhcp setup is valid
72
+ def handle_dhcp(zone_name, network_name, network_entry, address_start, address_end)
73
+ resource_dhcp = Wire::Resource::ResourceFactory
74
+ .instance.create(:dhcpconfig, "wire__#{zone_name}", network_name,
75
+ network_entry, address_start, address_end)
76
+ if resource_dhcp.down?
77
+ outputs 'DOWN', "dnsmasq/dhcp config on network \'#{network_name}\' is already down.", :ok2
78
+ return true
79
+ end
80
+
81
+ resource_dhcp.down
82
+ if resource_dhcp.down?
83
+ outputs 'DOWN', "dnsmasq/dhcp config on network \'#{network_name}\' is down.", :ok
84
+ state.update(:dnsmasq, network_name, :down)
85
+ return true
86
+ else
87
+ outputs 'DOWN', 'Error unconfiguring dnsmasq/dhcp ' \
88
+ "config on network \'#{network_name}\'.", :err
89
+ return false
90
+ end
91
+ end
92
+
93
+ # take the appgroups' controller and directs methods to
94
+ # it. First checks if appgroup is down. If so, ok. If not, take it down
95
+ # and ensure that it's down
96
+ # Params:
97
+ # +zone_name+:: Name of zone
98
+ # +appgroup_name+:: Name of Appgroup
99
+ # +appgroup_entry+:: Appgroup data from model
100
+ # +target_dir+:: Target directory (where fig file is located)
101
+ def handle_appgroup(zone_name, appgroup_name, appgroup_entry, target_dir)
102
+ # get path
103
+ controller_entry = appgroup_entry[:controller]
104
+
105
+ if controller_entry[:type] == 'fig'
106
+ return handle_appgroup__fig(zone_name, appgroup_name, appgroup_entry, target_dir)
107
+ end
108
+
109
+ $log.error "Appgroup not handled for zone #{zone_name}, " \
110
+ "unknown controller type #{controller_entry[:type]}"
111
+ false
112
+ end
113
+
114
+ # implement appgroup handling for fig controller
115
+ # Params:
116
+ # +zone_name+:: Name of zone
117
+ # +appgroup_name+:: Name of Appgroup
118
+ # +appgroup_entry+:: Appgroup data from model
119
+ # +target_dir+:: Target directory (where fig file is located)
120
+ def handle_appgroup__fig(zone_name, appgroup_name, appgroup_entry, target_dir)
121
+ # get path
122
+ controller_entry = appgroup_entry[:controller]
123
+
124
+ fig_path = File.join(File.expand_path(target_dir), controller_entry[:file])
125
+
126
+ resource_fig = Wire::Resource::ResourceFactory
127
+ .instance.create(:figadapter, "#{appgroup_name}", fig_path)
128
+
129
+ if resource_fig.down?
130
+ outputs 'DOWN', "appgroup \'#{appgroup_name}\' for zone #{zone_name} is already down.", :ok2
131
+ return true
132
+ end
133
+
134
+ b_result = false
135
+ resource_fig.down
136
+ if resource_fig.down?
137
+ outputs 'DOWN', "appgroup \'#{appgroup_name}\' for zone #{zone_name} is down.", :ok
138
+ state.update(:appgroup, appgroup_name, :down)
139
+ b_result = true
140
+ else
141
+ outputs 'DOWN', "Error taking down appgroup \'#{appgroup_name}\' for zone #{zone_name}.",
142
+ :err
143
+ b_result = false
144
+ end
145
+
146
+ b_result
147
+ end
148
+
149
+ # detaches networks to containers of appgroup
150
+ # Params:
151
+ # ++_zone_name++: Name of zone
152
+ # ++networks++: Array of networks names, what to attach
153
+ # ++appgroup_name++: Name of appgroup
154
+ # ++appgroup_entry++: appgroup hash
155
+ # ++target_dir++: project target dir
156
+ # Returns
157
+ # - [Bool] true if appgroup setup is ok
158
+ def handle_network_attachments(_zone_name, networks, appgroup_name,
159
+ appgroup_entry, target_dir)
160
+ # query container ids of containers running here
161
+ # get path
162
+ controller_entry = appgroup_entry[:controller]
163
+
164
+ container_ids = []
165
+
166
+ if controller_entry[:type] == 'fig'
167
+ fig_path = File.join(File.expand_path(target_dir), controller_entry[:file])
168
+
169
+ resource_fig = Wire::Resource::ResourceFactory
170
+ .instance.create(:figadapter, "#{appgroup_name}", fig_path)
171
+
172
+ container_ids = resource_fig.up_ids || []
173
+ $log.debug "Got #{container_ids.size} container id(s) from adapter"
174
+ end
175
+
176
+ #
177
+ resource_nw = Wire::Resource::ResourceFactory
178
+ .instance.create(:networkinjection, appgroup_name, networks.keys, container_ids)
179
+ if resource_nw.down?
180
+ outputs 'DOWN', "appgroup \'#{appgroup_name}\' network " \
181
+ 'attachments already detached.', :ok2
182
+ state.update(:appgroup, appgroup_name, :down)
183
+ return true
184
+ else
185
+ resource_nw.down
186
+ if resource_nw.down?
187
+ outputs 'DOWN', "appgroup \'#{appgroup_name}\' detached " \
188
+ "networks #{networks.keys.join(',')}.", :ok
189
+ state.update(:appgroup, appgroup_name, :down)
190
+ return true
191
+ else
192
+ outputs 'DOWN', "Error detaching networks to appgroup \'#{appgroup_name}\'.",
193
+ :err
194
+ return false
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,89 @@
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
+ # Init Command opens an interactive console dialog
10
+ # for initializing a model structure
11
+ # params:
12
+ # - :target_dir
13
+ class InitCommand < BaseCommand
14
+ def run(params = {})
15
+ puts "Initializing model in #{params[:target_dir]}"
16
+
17
+ model_data = {}
18
+ zone_data = {}
19
+ network_data = {}
20
+ model_data.store :zones, zone_data
21
+ model_data.store :networks, network_data
22
+
23
+ zone_names = InitInteractive.ask_for_zone_names
24
+ if zone_names.size == 0
25
+ $stderr.puts 'ERROR: must at least have one zone'
26
+ exit Wire.cli_exitcode(:init_bad_input)
27
+ end
28
+ zone_names.each do |zone_name|
29
+
30
+ zone_detail = {
31
+ :desc => 'Enter a short description here',
32
+ :long_desc => 'Enter a longer description here'
33
+ }
34
+
35
+ zone_data.store zone_name, zone_detail
36
+
37
+ networks = InitInteractive.ask_for_network_in_zone zone_name
38
+ networks.each do |network_name|
39
+
40
+ network_details = InitInteractive.ask_detail_data_for_network network_name
41
+ network_details.merge!({ :zone => zone_name })
42
+ network_data.store network_name, network_details
43
+ end
44
+ end
45
+
46
+ # write resulting model to file
47
+ export_model_file(model_data, params[:target_dir])
48
+ end
49
+
50
+ # Given a model structure and a target dir, this method
51
+ # ensures that targetdir exists and structure contents
52
+ # are written to individual files
53
+ def export_model_file(model_data, target_dir)
54
+ puts "Target dir is #{target_dir}"
55
+
56
+ # ensure target_dir exists
57
+ if File.exist?(target_dir)
58
+ if File.directory?(target_dir)
59
+ $stdout.puts "Writing output to #{target_dir}"
60
+ else
61
+ $stderr.puts "ERROR: Target dir #{target_dir} exists, " \
62
+ 'but is not a directory.'
63
+ exit Wire.cli_exitcode(:init_dir_error)
64
+ end
65
+ else
66
+ begin
67
+ FileUtils.mkdir_p(target_dir)
68
+ rescue => excpt
69
+ $stderr.puts "ERROR: Unable to create #{target_dir}: #{excpt}"
70
+ exit Wire.cli_exitcode(:init_dir_error)
71
+ end
72
+ end
73
+
74
+ [:zones, :networks].each do |element_sym|
75
+ element = model_data[element_sym]
76
+ export_element_file element_sym, element, target_dir
77
+ end
78
+ end
79
+
80
+ # exports an element part of the model to a single file
81
+ # in target_dir, as yaml.
82
+ def export_element_file(element_sym, element_data, target_dir)
83
+ filename = File.join(target_dir, "#{element_sym}.yaml")
84
+ open(filename, 'w') do |out_file|
85
+ out_file.puts(element_data.to_yaml)
86
+ end
87
+ end
88
+ end
89
+ end