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,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