junos-ez-stdlib 0.0.10
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.
- data/LICENSE +26 -0
- data/README.md +181 -0
- data/docs/Config_Utils.md +3 -0
- data/docs/Facts.md +106 -0
- data/docs/Filesys_Utils.md +3 -0
- data/docs/IPports.md +3 -0
- data/docs/L1ports.md +3 -0
- data/docs/L2ports.md +3 -0
- data/docs/Providers_Resources.md +304 -0
- data/docs/RE_utils.md +3 -0
- data/docs/StaticHosts.md +3 -0
- data/docs/StaticRoutes.md +3 -0
- data/docs/Vlans.md +3 -0
- data/examples/config/config_file.rb +72 -0
- data/examples/config/config_template_object.rb +81 -0
- data/examples/config/config_template_simple.rb +76 -0
- data/examples/config/multi_config.rb +60 -0
- data/examples/fs_utils.rb +31 -0
- data/examples/re_upgrade.rb +90 -0
- data/examples/re_utils.rb +30 -0
- data/examples/simple.rb +47 -0
- data/examples/st_hosts.rb +33 -0
- data/examples/vlans.rb +25 -0
- data/junos-ez-stdlib.gemspec +15 -0
- data/lib/junos-ez/facts/chassis.rb +45 -0
- data/lib/junos-ez/facts/ifd_style.rb +14 -0
- data/lib/junos-ez/facts/personality.rb +22 -0
- data/lib/junos-ez/facts/switch_style.rb +22 -0
- data/lib/junos-ez/facts/version.rb +32 -0
- data/lib/junos-ez/facts.rb +85 -0
- data/lib/junos-ez/ip_ports/classic.rb +149 -0
- data/lib/junos-ez/ip_ports.rb +28 -0
- data/lib/junos-ez/l1_ports/classic.rb +87 -0
- data/lib/junos-ez/l1_ports/switch.rb +134 -0
- data/lib/junos-ez/l1_ports.rb +81 -0
- data/lib/junos-ez/l2_ports/bridge_domain.rb +0 -0
- data/lib/junos-ez/l2_ports/vlan.rb +317 -0
- data/lib/junos-ez/l2_ports/vlan_l2ng.rb +0 -0
- data/lib/junos-ez/l2_ports.rb +57 -0
- data/lib/junos-ez/provider.rb +608 -0
- data/lib/junos-ez/stdlib.rb +16 -0
- data/lib/junos-ez/system/st_hosts.rb +74 -0
- data/lib/junos-ez/system/st_routes.rb +135 -0
- data/lib/junos-ez/system/syscfg.rb +103 -0
- data/lib/junos-ez/system.rb +98 -0
- data/lib/junos-ez/utils/config.rb +205 -0
- data/lib/junos-ez/utils/fs.rb +376 -0
- data/lib/junos-ez/utils/re.rb +371 -0
- data/lib/junos-ez/vlans/bridge_domain.rb +85 -0
- data/lib/junos-ez/vlans/vlan.rb +112 -0
- data/lib/junos-ez/vlans.rb +31 -0
- metadata +111 -0
@@ -0,0 +1,608 @@
|
|
1
|
+
##### ---------------------------------------------------------------
|
2
|
+
##### The Junos::Ez::Provider and associated Parent class make up
|
3
|
+
##### the main 'framework' of the "EZ" library system. Please
|
4
|
+
##### consider your changes carefully as it will have a large
|
5
|
+
##### scope of impact. Thank you.
|
6
|
+
##### ---------------------------------------------------------------
|
7
|
+
|
8
|
+
module Junos; end
|
9
|
+
|
10
|
+
module Junos::Ez
|
11
|
+
|
12
|
+
VERSION = "0.0.10"
|
13
|
+
|
14
|
+
### ---------------------------------------------------------------
|
15
|
+
### rpc_errors - decodes the XML into an array of error/Hash
|
16
|
+
### @@@ TBD: this should be moved into the 'netconf' gem
|
17
|
+
### ---------------------------------------------------------------
|
18
|
+
|
19
|
+
def self.rpc_errors( as_xml )
|
20
|
+
errs = as_xml.xpath('//rpc-error')
|
21
|
+
return nil if errs.count == 0 # safety check
|
22
|
+
|
23
|
+
retval = []
|
24
|
+
errs.each do |err|
|
25
|
+
err_h = {}
|
26
|
+
# every error has a severity and message
|
27
|
+
err_h[:severity] = err.xpath('error-severity').text.strip
|
28
|
+
err_h[:message] = err.xpath('error-message').text.strip
|
29
|
+
|
30
|
+
# some have an edit path error
|
31
|
+
unless ( err_path = err.xpath('error-path')).empty?
|
32
|
+
err_h[:edit_path] = err_path.text.strip
|
33
|
+
end
|
34
|
+
|
35
|
+
# some have addition error-info/bad-element ...
|
36
|
+
unless ( bad_i = err.xpath('error-info/bad-element')).empty?
|
37
|
+
err_h[:bad_identifier] = bad_i.text.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
retval << err_h
|
41
|
+
end
|
42
|
+
retval
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
module Junos::Ez::Provider
|
48
|
+
|
49
|
+
## all managed objects have the following properties:
|
50
|
+
|
51
|
+
PROPERTIES = [
|
52
|
+
:_exist, # exists in configuration (or should)
|
53
|
+
:_active # active in configuration (or should)
|
54
|
+
]
|
55
|
+
|
56
|
+
## 'attach_instance_variable' is the way to dynamically
|
57
|
+
## add an instance variable to the on_obj and "publish"
|
58
|
+
## it in the same way attr_accessor would.
|
59
|
+
|
60
|
+
def self.attach_instance_variable( on_obj, varsname, new_obj )
|
61
|
+
ivar = ("@" + varsname.to_s).to_sym
|
62
|
+
on_obj.instance_variable_set( ivar, new_obj )
|
63
|
+
on_obj.define_singleton_method( varsname ) do
|
64
|
+
on_obj.instance_variable_get( ivar )
|
65
|
+
end
|
66
|
+
on_obj.providers << varsname
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class Junos::Ez::Provider::Parent
|
72
|
+
|
73
|
+
attr_reader :ndev, :parent, :name
|
74
|
+
attr_accessor :providers
|
75
|
+
attr_accessor :has, :should, :properties
|
76
|
+
attr_accessor :list, :catalog
|
77
|
+
|
78
|
+
# p_obj - the parent object
|
79
|
+
# name - the name of the resource, or nil if this is a provider
|
80
|
+
# opts - options to the provider/resource. :parent is reserved
|
81
|
+
|
82
|
+
def initialize( p_obj, name = nil, opts = {} )
|
83
|
+
|
84
|
+
@providers = []
|
85
|
+
@parent = opts[:parent] || nil
|
86
|
+
@ndev = p_obj.instance_variable_get(:@ndev) || p_obj
|
87
|
+
@name = name
|
88
|
+
@opts = opts
|
89
|
+
|
90
|
+
@list = [] # array list of item names
|
91
|
+
@catalog = {} # hash catalog of named items
|
92
|
+
|
93
|
+
return unless @name
|
94
|
+
# resources only from here ...
|
95
|
+
@has = {} # properties read-from Junos
|
96
|
+
@should = {} # properties to write-back to Junos
|
97
|
+
end
|
98
|
+
|
99
|
+
### ---------------------------------------------------------------
|
100
|
+
### 'is_provider?' - indicates if this object instance is a
|
101
|
+
### provider object, rather than a specific instance of the object
|
102
|
+
### ---------------------------------------------------------------
|
103
|
+
|
104
|
+
def is_provider?; @name.nil? end
|
105
|
+
|
106
|
+
### ---------------------------------------------------------------
|
107
|
+
### is_new? - indicates if this is a new resource
|
108
|
+
### ---------------------------------------------------------------
|
109
|
+
|
110
|
+
def is_new?; (@has[:_exist] == false) || false end
|
111
|
+
|
112
|
+
### ---------------------------------------------------------------
|
113
|
+
### [property] resource property reader or
|
114
|
+
### ["name"] resource selector from provider
|
115
|
+
### ---------------------------------------------------------------
|
116
|
+
|
117
|
+
def []( property )
|
118
|
+
return self.select( property ) if is_provider?
|
119
|
+
|
120
|
+
# if there is already something in the write-back, then use
|
121
|
+
# it before using from the read-cache
|
122
|
+
|
123
|
+
return @should[property] if @should[property]
|
124
|
+
return @has[property] if @has
|
125
|
+
end
|
126
|
+
|
127
|
+
### ---------------------------------------------------------------
|
128
|
+
### []= property writer (@should)
|
129
|
+
### ---------------------------------------------------------------
|
130
|
+
|
131
|
+
def []=( property, rval )
|
132
|
+
raise ArgumentError, "This is not a provider instance" if is_provider?
|
133
|
+
raise ArgumentError, "Invalid property['#{property.to_s}']" unless properties.include? property
|
134
|
+
|
135
|
+
@should[property] = rval
|
136
|
+
end
|
137
|
+
|
138
|
+
### ---------------------------------------------------------------
|
139
|
+
### 'select' a resource from a provider
|
140
|
+
### ---------------------------------------------------------------
|
141
|
+
|
142
|
+
def select( name )
|
143
|
+
raise ArgumentError, "This is not a provider instance" unless is_provider?
|
144
|
+
this = self.class.new( @ndev, name, @opts )
|
145
|
+
this.properties = self.properties
|
146
|
+
this.read!
|
147
|
+
this
|
148
|
+
end
|
149
|
+
|
150
|
+
### ---------------------------------------------------------------
|
151
|
+
### 'exists?' - does the resource exist in the Juos config
|
152
|
+
### ---------------------------------------------------------------
|
153
|
+
|
154
|
+
def exists?; @has[:_exist]; end
|
155
|
+
|
156
|
+
### ---------------------------------------------------------------
|
157
|
+
### 'active?' - is the resource config active in Junos
|
158
|
+
### ---------------------------------------------------------------
|
159
|
+
|
160
|
+
def active?
|
161
|
+
false unless exists?
|
162
|
+
@has[:_active]
|
163
|
+
end
|
164
|
+
|
165
|
+
### @@@ helper method, probably needs to go into 'private section
|
166
|
+
### @@@ TBD
|
167
|
+
|
168
|
+
def name_decorated( name = @name )
|
169
|
+
self.class.to_s + "['" + name + "']"
|
170
|
+
end
|
171
|
+
|
172
|
+
### ---------------------------------------------------------------
|
173
|
+
### Provider methods to obtain collection information as
|
174
|
+
### 'list' - array of named items
|
175
|
+
### 'catalog' - hash of all items with properties
|
176
|
+
### ---------------------------------------------------------------
|
177
|
+
|
178
|
+
def list
|
179
|
+
@list.empty? ? list! : @list
|
180
|
+
end
|
181
|
+
|
182
|
+
def list!
|
183
|
+
@list.clear
|
184
|
+
@list = build_list
|
185
|
+
end
|
186
|
+
|
187
|
+
def catalog
|
188
|
+
@catalog.empty? ? catalog! : @catalog
|
189
|
+
end
|
190
|
+
|
191
|
+
def catalog!
|
192
|
+
@catalog.clear
|
193
|
+
@catalog = build_catalog
|
194
|
+
end
|
195
|
+
|
196
|
+
### ---------------------------------------------------------------
|
197
|
+
### CREATE methods
|
198
|
+
### ---------------------------------------------------------------
|
199
|
+
|
200
|
+
## ----------------------------------------------------------------
|
201
|
+
## 'create' will build a new object, but does not write the
|
202
|
+
## contents back to the device. The caller can chain the
|
203
|
+
## write! method if desired Alternative, the caller
|
204
|
+
## can use 'create!' which does write to the device.
|
205
|
+
## ----------------------------------------------------------------
|
206
|
+
|
207
|
+
def create( name = nil, prop_hash = {}, &block )
|
208
|
+
|
209
|
+
## if this is an existing object, then we shouldn't
|
210
|
+
## allow the caller to create something.
|
211
|
+
|
212
|
+
raise ArgumentError, "Not called by provider!" unless is_provider?
|
213
|
+
|
214
|
+
## if we're here, then we're creating an entirely new
|
215
|
+
## instance of this object. We should check to see if
|
216
|
+
## it first exists, eh? So allow the caller to specify
|
217
|
+
## if they want an exception if it already exists; overloading
|
218
|
+
## the use of the prop_hash[:_exist], yo!
|
219
|
+
|
220
|
+
newbie = self.select( name )
|
221
|
+
if prop_hash[:_exist]
|
222
|
+
raise ArgumentError, name_decorated(name) + " already exists" if newbie.exists?
|
223
|
+
end
|
224
|
+
|
225
|
+
prop_hash.each{ |k,v| newbie[k] = v } unless prop_hash.empty?
|
226
|
+
|
227
|
+
## default mark the newly created object as should exist and should
|
228
|
+
## be active (if not already set)
|
229
|
+
|
230
|
+
newbie[:_exist] = true
|
231
|
+
newbie[:_active] ||= true
|
232
|
+
|
233
|
+
## if a block is provided, then pass the block the new object
|
234
|
+
## the caller is then expected to set the properies
|
235
|
+
|
236
|
+
yield( newbie ) if block_given?
|
237
|
+
|
238
|
+
## return the new object
|
239
|
+
return newbie
|
240
|
+
end
|
241
|
+
|
242
|
+
## ----------------------------------------------------------------
|
243
|
+
## 'create!' is just a helper to call create and then write
|
244
|
+
## the config assuming create returns ok.
|
245
|
+
## ----------------------------------------------------------------
|
246
|
+
|
247
|
+
def create!( name = nil, prop_hash = {}, &block )
|
248
|
+
newbie = create( name, prop_hash, block )
|
249
|
+
return nil unless newbie
|
250
|
+
newbie.write!
|
251
|
+
newbie
|
252
|
+
end
|
253
|
+
|
254
|
+
## ----------------------------------------------------------------
|
255
|
+
## YAML / HASH methods
|
256
|
+
## ----------------------------------------------------------------
|
257
|
+
|
258
|
+
def create_from_yaml!( opts = {} )
|
259
|
+
raise ArgumentError "Missing :filename param" unless opts[:filename]
|
260
|
+
as_hash = YAML.load_file( opts[:filename] )
|
261
|
+
write_xml_config! xml_from_h_expanded( as_hash, opts )
|
262
|
+
end
|
263
|
+
|
264
|
+
def create_from_hash!( as_hash, opts = {} )
|
265
|
+
write_xml_config! xml_from_h_expanded( as_hash, opts )
|
266
|
+
end
|
267
|
+
|
268
|
+
def to_h_expanded( opts = {} )
|
269
|
+
to_h( opts )
|
270
|
+
end
|
271
|
+
|
272
|
+
def to_yaml( opts = {} )
|
273
|
+
out_hash = to_h_expanded( opts )
|
274
|
+
out_yaml = out_hash.to_yaml
|
275
|
+
File.open( opts[:filename], "w" ){|f| f.puts out_hash.to_yaml } if opts[:filename]
|
276
|
+
out_yaml
|
277
|
+
end
|
278
|
+
|
279
|
+
### ---------------------------------------------------------------
|
280
|
+
### 'delete!' will cause the item to be removed from the Junos
|
281
|
+
### configuration
|
282
|
+
### ---------------------------------------------------------------
|
283
|
+
|
284
|
+
def delete!
|
285
|
+
return nil unless exists?
|
286
|
+
xml = xml_at_top
|
287
|
+
par = xml.instance_variable_get(:@parent)
|
288
|
+
par['delete'] = 'delete'
|
289
|
+
rsp = write_xml_config!( xml.doc.root )
|
290
|
+
@has[:_exist] = false
|
291
|
+
true # rsp ... don't return XML, but let's hear from the community...
|
292
|
+
end
|
293
|
+
|
294
|
+
### ---------------------------------------------------------------
|
295
|
+
### Junos activation controls
|
296
|
+
### ---------------------------------------------------------------
|
297
|
+
|
298
|
+
def activate!
|
299
|
+
return nil if @should[:_active] == true
|
300
|
+
@should[:_active] = true
|
301
|
+
write!
|
302
|
+
end
|
303
|
+
|
304
|
+
def deactivate!
|
305
|
+
return nil if @should[:_active] == false
|
306
|
+
@should[:_active] = false
|
307
|
+
write!
|
308
|
+
end
|
309
|
+
|
310
|
+
### ---------------------------------------------------------------
|
311
|
+
### Junos rename element
|
312
|
+
### ---------------------------------------------------------------
|
313
|
+
|
314
|
+
## by default, simply allow the new name
|
315
|
+
def xml_element_newname( new_name); new_name end
|
316
|
+
|
317
|
+
def rename!( new_name )
|
318
|
+
return nil unless exists?
|
319
|
+
|
320
|
+
xml = xml_at_top
|
321
|
+
par = xml.instance_variable_get(:@parent)
|
322
|
+
new_ele_name = xml_element_newname( new_name )
|
323
|
+
|
324
|
+
return nil unless new_ele_name
|
325
|
+
|
326
|
+
par['rename'] = 'rename'
|
327
|
+
par['name'] = new_ele_name
|
328
|
+
|
329
|
+
rsp = write_xml_config!( xml.doc.root )
|
330
|
+
@name = new_name
|
331
|
+
rsp
|
332
|
+
end
|
333
|
+
|
334
|
+
### ---------------------------------------------------------------
|
335
|
+
### Junos reorder method
|
336
|
+
###
|
337
|
+
### opts[:before] = item-name,
|
338
|
+
### opts[:after] = item-name
|
339
|
+
### ---------------------------------------------------------------
|
340
|
+
|
341
|
+
def reorder!( opts )
|
342
|
+
return nil unless exists?
|
343
|
+
|
344
|
+
## validate opts hash
|
345
|
+
ctrl, name = opts.first
|
346
|
+
raise ArgumentError, "Invalid operation #{ctrl}" unless [:before,:after].include? ctrl
|
347
|
+
|
348
|
+
xml = xml_at_top
|
349
|
+
par = xml.instance_variable_get(:@parent)
|
350
|
+
par['insert'] = ctrl.to_s
|
351
|
+
par['name'] = name
|
352
|
+
rsp = write_xml_config! ( xml.doc.root )
|
353
|
+
|
354
|
+
return rsp
|
355
|
+
end
|
356
|
+
|
357
|
+
### ---------------------------------------------------------------
|
358
|
+
### Provider each method - this will go and create a managed
|
359
|
+
### object for each item in the list. This could get CPU
|
360
|
+
### intensive depending on the number of items under provider
|
361
|
+
### management, yo!
|
362
|
+
### ---------------------------------------------------------------
|
363
|
+
|
364
|
+
def each( &block )
|
365
|
+
raise ArgumentError, "not a provider" unless is_provider?
|
366
|
+
list.each{ |name| yield select(name ) }
|
367
|
+
end
|
368
|
+
|
369
|
+
### ---------------------------------------------------------------
|
370
|
+
### Provider reader methods
|
371
|
+
### ---------------------------------------------------------------
|
372
|
+
|
373
|
+
## 'init_has' is called when creating a new managed object
|
374
|
+
## or when a caller attempts to retrieve a non-existing one
|
375
|
+
|
376
|
+
def init_has; nil end
|
377
|
+
|
378
|
+
## 'xml_get_has_xml' - used to retrieve the starting location of the
|
379
|
+
## actual XML data for the managed object (as compared to the top
|
380
|
+
## of the configuration document
|
381
|
+
|
382
|
+
def xml_get_has_xml( xml ); nil end
|
383
|
+
|
384
|
+
## 'xml_config_read!' is ued to retrieve the configuration
|
385
|
+
## from the Junos device
|
386
|
+
|
387
|
+
def xml_config_read!
|
388
|
+
@ndev.rpc.get_configuration( xml_at_top )
|
389
|
+
end
|
390
|
+
|
391
|
+
def read!
|
392
|
+
@has.clear
|
393
|
+
cfg_xml = xml_config_read!
|
394
|
+
@has_xml = xml_get_has_xml( cfg_xml )
|
395
|
+
|
396
|
+
## if the thing doesn't exist in Junos, then mark the @has
|
397
|
+
## structure accordingly and call the object init_has for
|
398
|
+
## any defaults
|
399
|
+
|
400
|
+
unless @has_xml
|
401
|
+
@has[:_exist] ||= false
|
402
|
+
@has[:_active] ||= true
|
403
|
+
init_has
|
404
|
+
return nil
|
405
|
+
end
|
406
|
+
|
407
|
+
## xml_read_parser *MUST* be implmented by the provider class
|
408
|
+
## it is used to parse the XML into the HASH structure. It
|
409
|
+
## returns true/false
|
410
|
+
|
411
|
+
xml_read_parser( @has_xml, @has )
|
412
|
+
|
413
|
+
## return the Hash representation
|
414
|
+
self.has
|
415
|
+
end
|
416
|
+
|
417
|
+
### ---------------------------------------------------------------
|
418
|
+
### Provider writer methods
|
419
|
+
### ---------------------------------------------------------------
|
420
|
+
|
421
|
+
def need_write?; not @should.empty? end
|
422
|
+
|
423
|
+
def write!
|
424
|
+
return nil if @should.empty?
|
425
|
+
|
426
|
+
@should[:_exist] ||= true
|
427
|
+
|
428
|
+
# create the necessary chagnes and push them to the Junos
|
429
|
+
# device. If an error occurs, it will be raised
|
430
|
+
|
431
|
+
xml_change = xml_build_change
|
432
|
+
return nil unless xml_change
|
433
|
+
rsp = write_xml_config!( xml_change )
|
434
|
+
|
435
|
+
# copy the 'should' values into the 'has' values now that
|
436
|
+
# they've been written back to Junos
|
437
|
+
|
438
|
+
@has.merge! @should
|
439
|
+
@should.clear
|
440
|
+
|
441
|
+
# returning 'true' for now. might need to change this back
|
442
|
+
# to 'rsp' depending on the community feedback. general approach is to not have to
|
443
|
+
# deal with XML, unless it's an exception case. the only time rsp is really
|
444
|
+
# needed is to look at warnings; i.e. not-errors. errors will generate an exception, yo!
|
445
|
+
|
446
|
+
return true
|
447
|
+
end
|
448
|
+
|
449
|
+
### ---------------------------------------------------------------
|
450
|
+
### XML writer methods
|
451
|
+
### ---------------------------------------------------------------
|
452
|
+
|
453
|
+
def xml_at_edit; nil; end
|
454
|
+
def xml_at_top; nil; end
|
455
|
+
def xml_on_create( xml ); nil; end
|
456
|
+
def xml_on_delete( xml ); nil; end
|
457
|
+
|
458
|
+
def xml_change__exist( xml )
|
459
|
+
return xml_on_create( xml ) if @should[:_exist]
|
460
|
+
|
461
|
+
par = xml.instance_variable_get(:@parent)
|
462
|
+
par['delete'] = 'delete'
|
463
|
+
|
464
|
+
return xml_on_delete( xml )
|
465
|
+
end
|
466
|
+
|
467
|
+
## 'xml_build_change' is used to create the Junos XML
|
468
|
+
## configuration structure. Generally speaking it
|
469
|
+
## should not be called by code outside the providers,
|
470
|
+
## but sometimes we might want to, so don't make it private
|
471
|
+
|
472
|
+
def xml_build_change( xml_at_here = nil )
|
473
|
+
edit_at = xml_at_here || xml_at_edit || xml_at_top
|
474
|
+
|
475
|
+
if @should[:_exist] == false
|
476
|
+
xml_change__exist( edit_at )
|
477
|
+
return edit_at.doc.root
|
478
|
+
end
|
479
|
+
|
480
|
+
changed = false
|
481
|
+
@should.keys.each do |prop|
|
482
|
+
changed = true if self.send( "xml_change_#{prop}", edit_at )
|
483
|
+
end
|
484
|
+
(changed) ? edit_at.doc.root : nil
|
485
|
+
end
|
486
|
+
|
487
|
+
### ---------------------------------------------------------------
|
488
|
+
### XML common write "change" methods
|
489
|
+
### ---------------------------------------------------------------
|
490
|
+
|
491
|
+
def xml_change_admin( xml )
|
492
|
+
xml.disable (@should[:admin] == :up ) ? Netconf::JunosConfig::DELETE : nil
|
493
|
+
end
|
494
|
+
|
495
|
+
def xml_change_description( xml )
|
496
|
+
xml_set_or_delete( xml, 'description', @should[:description] )
|
497
|
+
end
|
498
|
+
|
499
|
+
def xml_change__active( xml )
|
500
|
+
par = xml.instance_variable_get(:@parent)
|
501
|
+
value = @should[:_active] ? 'active' : 'inactive'
|
502
|
+
par[value] = value # attribute name is same as value
|
503
|
+
end
|
504
|
+
|
505
|
+
### ---------------------------------------------------------------
|
506
|
+
### 'to_h' lets us look at the read/write hash structures
|
507
|
+
### ---------------------------------------------------------------
|
508
|
+
|
509
|
+
def to_h( which = :read )
|
510
|
+
{ @name => (which == :read) ? @has : @should }
|
511
|
+
end
|
512
|
+
|
513
|
+
##### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
514
|
+
##### !!!!! PRIVATE METHODS !!!!!
|
515
|
+
##### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
516
|
+
|
517
|
+
private
|
518
|
+
|
519
|
+
def set_has_status( xml, has )
|
520
|
+
has[:_active] = xml['inactive'] ? false : true
|
521
|
+
has[:_exist] = true
|
522
|
+
end
|
523
|
+
|
524
|
+
### ---------------------------------------------------------------
|
525
|
+
### write configuration to Junos. Check for errors vs. warnings.
|
526
|
+
### if there are warnings then return the result. If there are
|
527
|
+
### errors, re-throw the exception object. If everything was
|
528
|
+
### OK, simply return the result
|
529
|
+
### ---------------------------------------------------------------
|
530
|
+
|
531
|
+
def write_xml_config!( xml, opts = {} )
|
532
|
+
begin
|
533
|
+
action = {'action' => 'replace' }
|
534
|
+
result = @ndev.rpc.load_configuration( xml, action )
|
535
|
+
rescue Netconf::RpcError => e
|
536
|
+
errs = e.rsp.xpath('//rpc-error[error-severity = "error"]')
|
537
|
+
raise e unless errs.empty?
|
538
|
+
e.rsp
|
539
|
+
else
|
540
|
+
result
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def oh_no!
|
545
|
+
return if @opts[:ignore_raise]
|
546
|
+
yield if block_given? # should always be a block given ...
|
547
|
+
end
|
548
|
+
|
549
|
+
### ---------------------------------------------------------------
|
550
|
+
### XML property reader/writer for elements that can be present,
|
551
|
+
### or existing with a "no-" prepended. For example "retain" or
|
552
|
+
### "no-retain"
|
553
|
+
### ---------------------------------------------------------------
|
554
|
+
|
555
|
+
def xml_read_parse_noele( as_xml, ele_name, as_hash, prop )
|
556
|
+
unless (ele = as_xml.xpath("#{ele_name} | no-#{ele_name}")).empty?
|
557
|
+
as_hash[prop] = (ele[0].name =~ /^no-/) ? false : true
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def xml_set_or_delete_noele( xml, ele_name, prop = ele_name.to_sym )
|
562
|
+
|
563
|
+
# delete what was there
|
564
|
+
unless @has[prop].nil?
|
565
|
+
value_prop = @has[prop]
|
566
|
+
wr_ele_name = value_prop ? ele_name : 'no-' + ele_name
|
567
|
+
xml.send(wr_ele_name.to_sym, Netconf::JunosConfig::DELETE)
|
568
|
+
end
|
569
|
+
|
570
|
+
# if we're not adding anything back, signal that we've done
|
571
|
+
# something, and we're done, yo!
|
572
|
+
return true if @should[prop].nil?
|
573
|
+
|
574
|
+
# add new value
|
575
|
+
value_prop = @should[prop]
|
576
|
+
ele_name = 'no-' + ele_name if value_prop == false
|
577
|
+
xml.send( ele_name.to_sym )
|
578
|
+
|
579
|
+
end
|
580
|
+
|
581
|
+
def xml_when_item( xml_item, &block )
|
582
|
+
raise ArgumentError, "no block given" unless block_given?
|
583
|
+
return unless xml_item[0]
|
584
|
+
return yield(xml_item[0]) if block.arity == 1
|
585
|
+
yield
|
586
|
+
end
|
587
|
+
|
588
|
+
### ---------------------------------------------------------------
|
589
|
+
### XML property writer utilities
|
590
|
+
### ---------------------------------------------------------------
|
591
|
+
|
592
|
+
def xml_set_or_delete( xml, ele_name, value )
|
593
|
+
xml.send( ele_name.to_sym, (value ? value : Netconf::JunosConfig::DELETE) )
|
594
|
+
end
|
595
|
+
|
596
|
+
def xml_set_or_delete_element( xml, ele_name, should )
|
597
|
+
xml.send( ele_name.to_sym, (should) ? nil : Netconf::JunosConfig::DELETE )
|
598
|
+
end
|
599
|
+
|
600
|
+
def diff_property_array( prop )
|
601
|
+
should = @should[prop] || []
|
602
|
+
has = @has[prop] || []
|
603
|
+
[ should - has, has - should ]
|
604
|
+
end
|
605
|
+
|
606
|
+
end
|
607
|
+
|
608
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require 'junos-ez/provider' # framework code
|
3
|
+
require 'junos-ez/facts' # fact keeper
|
4
|
+
require 'junos-ez/system' # various system resources
|
5
|
+
require 'junos-ez/l1_ports' # physical ports
|
6
|
+
require 'junos-ez/vlans' # vlans
|
7
|
+
require 'junos-ez/l2_ports' # switch ports
|
8
|
+
require 'junos-ez/ip_ports' # ip ports (v4)
|
9
|
+
|
10
|
+
# -------------------------------------------------------------------
|
11
|
+
# utility libraries, not providers
|
12
|
+
# -------------------------------------------------------------------
|
13
|
+
|
14
|
+
require 'junos-ez/utils/re'
|
15
|
+
require 'junos-ez/utils/fs'
|
16
|
+
require 'junos-ez/utils/config'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Junos::Ez::StaticHosts::Provider
|
2
|
+
|
3
|
+
### ---------------------------------------------------------------
|
4
|
+
### XML top placement
|
5
|
+
### ---------------------------------------------------------------
|
6
|
+
|
7
|
+
def xml_at_top
|
8
|
+
xml = Nokogiri::XML::Builder.new {|xml| xml.configuration {
|
9
|
+
xml.system { xml.send('static-host-mapping') {
|
10
|
+
xml.name @name
|
11
|
+
return xml
|
12
|
+
}}
|
13
|
+
}}
|
14
|
+
end
|
15
|
+
|
16
|
+
### ---------------------------------------------------------------
|
17
|
+
### XML property readers
|
18
|
+
### ---------------------------------------------------------------
|
19
|
+
|
20
|
+
def xml_get_has_xml( xml )
|
21
|
+
xml.xpath('//static-host-mapping')[0]
|
22
|
+
end
|
23
|
+
|
24
|
+
def xml_read_parser( as_xml, as_hash )
|
25
|
+
set_has_status( as_xml, as_hash )
|
26
|
+
|
27
|
+
ip_v4 = as_xml.xpath('inet').text
|
28
|
+
as_hash[:ip] = ip_v4 unless ip_v4.empty?
|
29
|
+
|
30
|
+
ip_v6 = as_xml.xpath('inet6').text
|
31
|
+
as_hash[:ip6] = ip_v6 unless ip_v6.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
### ---------------------------------------------------------------
|
35
|
+
### XML property writers
|
36
|
+
### ---------------------------------------------------------------
|
37
|
+
|
38
|
+
def xml_change_ip( xml )
|
39
|
+
xml_set_or_delete( xml, 'inet', @should[:ip] )
|
40
|
+
end
|
41
|
+
|
42
|
+
def xml_change_ip6( xml )
|
43
|
+
xml_set_or_delete( xml, 'inet6', @should[:ip6] )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
##### ---------------------------------------------------------------
|
49
|
+
##### Provider collection methods
|
50
|
+
##### ---------------------------------------------------------------
|
51
|
+
|
52
|
+
class Junos::Ez::StaticHosts::Provider
|
53
|
+
|
54
|
+
def build_list
|
55
|
+
@ndev.rpc.get_configuration{|xml| xml.system {
|
56
|
+
xml.send(:'static-host-mapping')
|
57
|
+
}}.xpath('system/static-host-mapping/name').collect do |item|
|
58
|
+
item.text
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_catalog
|
63
|
+
@catalog = {}
|
64
|
+
@ndev.rpc.get_configuration{ |xml| xml.system {
|
65
|
+
xml.send(:'static-host-mapping')
|
66
|
+
}}.xpath('system/static-host-mapping').each do |item|
|
67
|
+
name = item.xpath('name').text
|
68
|
+
@catalog[name] = {}
|
69
|
+
xml_read_parser( item, @catalog[name] )
|
70
|
+
end
|
71
|
+
@catalog
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|