shopify-junos-ez-stdlib 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +91 -0
  3. data/LICENSE +26 -0
  4. data/README.md +199 -0
  5. data/docs/Facts.md +192 -0
  6. data/docs/Providers/Group.md +61 -0
  7. data/docs/Providers/IPports.md +61 -0
  8. data/docs/Providers/L1ports.md +29 -0
  9. data/docs/Providers/L2ports.md +43 -0
  10. data/docs/Providers/LAGports.md +57 -0
  11. data/docs/Providers/StaticHosts.md +26 -0
  12. data/docs/Providers/StaticRoutes.md +37 -0
  13. data/docs/Providers/UserAuths.md +32 -0
  14. data/docs/Providers/Users.md +122 -0
  15. data/docs/Providers/Vlans.md +43 -0
  16. data/docs/Providers_Resources.md +353 -0
  17. data/docs/README_FIRST.md +27 -0
  18. data/docs/Utils/Config.md +160 -0
  19. data/docs/Utils/Filesystem.md +360 -0
  20. data/docs/Utils/Routing-Engine.md +379 -0
  21. data/docs/Utils/SCP.md +24 -0
  22. data/examples/config/config_file.rb +72 -0
  23. data/examples/config/config_template_object.rb +81 -0
  24. data/examples/config/config_template_simple.rb +76 -0
  25. data/examples/config/multi_config.rb +60 -0
  26. data/examples/fs_utils.rb +31 -0
  27. data/examples/lag_port.rb +27 -0
  28. data/examples/re_upgrade.rb +99 -0
  29. data/examples/re_utils.rb +33 -0
  30. data/examples/simple.rb +46 -0
  31. data/examples/st_hosts.rb +33 -0
  32. data/examples/user.rb +32 -0
  33. data/examples/vlans.rb +31 -0
  34. data/junos-ez-stdlib.gemspec +15 -0
  35. data/lib/junos-ez/exceptions.rb +3 -0
  36. data/lib/junos-ez/facts.rb +83 -0
  37. data/lib/junos-ez/facts/chassis.rb +51 -0
  38. data/lib/junos-ez/facts/ifd_style.rb +17 -0
  39. data/lib/junos-ez/facts/personality.rb +25 -0
  40. data/lib/junos-ez/facts/switch_style.rb +31 -0
  41. data/lib/junos-ez/facts/version.rb +58 -0
  42. data/lib/junos-ez/group.rb +206 -0
  43. data/lib/junos-ez/ip_ports.rb +30 -0
  44. data/lib/junos-ez/ip_ports/classic.rb +188 -0
  45. data/lib/junos-ez/l1_ports.rb +121 -0
  46. data/lib/junos-ez/l1_ports/classic.rb +87 -0
  47. data/lib/junos-ez/l1_ports/switch.rb +134 -0
  48. data/lib/junos-ez/l2_ports.rb +66 -0
  49. data/lib/junos-ez/l2_ports/bridge_domain.rb +499 -0
  50. data/lib/junos-ez/l2_ports/vlan.rb +433 -0
  51. data/lib/junos-ez/l2_ports/vlan_l2ng.rb +502 -0
  52. data/lib/junos-ez/lag_ports.rb +268 -0
  53. data/lib/junos-ez/provider.rb +619 -0
  54. data/lib/junos-ez/stdlib.rb +18 -0
  55. data/lib/junos-ez/system.rb +48 -0
  56. data/lib/junos-ez/system/st_hosts.rb +92 -0
  57. data/lib/junos-ez/system/st_routes.rb +159 -0
  58. data/lib/junos-ez/system/syscfg.rb +103 -0
  59. data/lib/junos-ez/system/userauths.rb +84 -0
  60. data/lib/junos-ez/system/users.rb +217 -0
  61. data/lib/junos-ez/utils/config.rb +236 -0
  62. data/lib/junos-ez/utils/fs.rb +385 -0
  63. data/lib/junos-ez/utils/re.rb +558 -0
  64. data/lib/junos-ez/version.rb +6 -0
  65. data/lib/junos-ez/vlans.rb +38 -0
  66. data/lib/junos-ez/vlans/bridge_domain.rb +89 -0
  67. data/lib/junos-ez/vlans/vlan.rb +119 -0
  68. data/lib/junos-ez/vlans/vlan_l2ng.rb +126 -0
  69. data/shipit.yml +4 -0
  70. data/tmp +7 -0
  71. metadata +129 -0
@@ -0,0 +1,206 @@
1
+ require "junos-ez/provider"
2
+
3
+ module Junos::Ez::Group
4
+
5
+ PROPERTIES = [
6
+ :format, # [:set, :text, :xml]
7
+ :path, # Configuration file path
8
+ ]
9
+
10
+ def self.Provider( ndev, varsym )
11
+ newbie = Junos::Ez::Group::Provider::new( ndev )
12
+ newbie.properties = Junos::Ez::Provider::PROPERTIES + PROPERTIES
13
+ Junos::Ez::Provider.attach_instance_variable( ndev, varsym, newbie )
14
+ end
15
+
16
+ class Provider < Junos::Ez::Provider::Parent
17
+ # common parenting goes here ... if we were to
18
+ # subclass the objects ... not doing that now
19
+ end
20
+
21
+ end
22
+
23
+ class Junos::Ez::Group::Provider
24
+
25
+ ### ---------------------------------------------------------------
26
+ ### XML top placement
27
+ ### ---------------------------------------------------------------
28
+
29
+ def xml_at_top
30
+ xml = Nokogiri::XML::Builder.new {|xml| xml.configuration {
31
+ xml.groups {
32
+ xml.name @name
33
+ return xml
34
+ }
35
+ }}
36
+ end
37
+
38
+ ### ---------------------------------------------------------------
39
+ ### XML property readers
40
+ ### ---------------------------------------------------------------
41
+
42
+ def xml_get_has_xml( xml )
43
+ xml.xpath('//groups')[0]
44
+ end
45
+
46
+ def xml_read_parser( as_xml, as_hash )
47
+ set_has_status( as_xml, as_hash )
48
+
49
+ grp = as_xml.xpath('name').text
50
+ as_hash[:name] = grp unless grp.empty?
51
+
52
+ end
53
+
54
+
55
+ ### ---------------------------------------------------------------
56
+ ### XML property writers
57
+ ### ---------------------------------------------------------------
58
+
59
+ def xml_change_path( xml )
60
+ end
61
+
62
+ def xml_change_format( xml )
63
+ end
64
+
65
+ ### ---------------------------------------------------------------
66
+ ### XML on-create
67
+ ### ---------------------------------------------------------------
68
+
69
+ def xml_on_create( xml )
70
+ end
71
+
72
+ ### ---------------------------------------------------------------
73
+ ### XML on-delete
74
+ ### ---------------------------------------------------------------
75
+ def xml_on_delete( xml )
76
+ end
77
+
78
+ def write_xml_config!( xml, opts = {} )
79
+ if (@should[:_exist] == true)
80
+ _load ( xml )
81
+ @should[:format] = 'xml' unless @should[:format]
82
+ begin
83
+ attr = {}
84
+ attr[:action] = 'replace'
85
+ attr[:format] = @should[:format].to_s
86
+ result = @ndev.rpc.load_configuration( @config.to_s, attr )
87
+ rescue Netconf::RpcError => e
88
+ errs = e.rsp.xpath('//rpc-error[error-severity = "error"]')
89
+ raise e unless errs.empty?
90
+ e.rsp
91
+ else
92
+ result
93
+ end
94
+ else
95
+ super(xml)
96
+ end
97
+ _apply_group
98
+ end
99
+
100
+ def write!
101
+ return nil if @should.empty?
102
+
103
+ @should[:_exist] ||= true
104
+ @should[:_active] ||= :true
105
+ # load the conifguration from file and apply under group
106
+ # hirerachy
107
+ rsp = write_xml_config!( xml_at_top.doc.root )
108
+
109
+ # copy the 'should' values into the 'has' values now that
110
+ # they've been written back to Junos
111
+
112
+ @has.merge! @should
113
+ @should.clear
114
+
115
+ return true
116
+ end
117
+
118
+ end
119
+
120
+
121
+ ##### ---------------------------------------------------------------
122
+ ##### Provider collection methods
123
+ ##### ---------------------------------------------------------------
124
+
125
+ class Junos::Ez::Group::Provider
126
+
127
+ def build_list
128
+ grp_cfgs = @ndev.rpc.get_configuration{|xml|
129
+ xml.send(:'groups')
130
+ }.xpath('groups/name').collect do |item|
131
+ item.text
132
+ end
133
+ return grp_cfgs
134
+ end
135
+
136
+ def build_catalog
137
+ return @catalog if list!.empty?
138
+ list.each do |grp_name|
139
+ @ndev.rpc.get_configuration{ |xml|
140
+ xml.groups {
141
+ xml.name grp_name
142
+ }
143
+ }.xpath('groups').each do |as_xml|
144
+ @catalog[grp_name] = {}
145
+ xml_read_parser( as_xml, @catalog[grp_name] )
146
+ end
147
+ end
148
+ @catalog
149
+ end
150
+ end
151
+
152
+ ##### ---------------------------------------------------------------
153
+ ##### _PRIVATE methods
154
+ ##### ---------------------------------------------------------------
155
+
156
+ class Junos::Ez::Group::Provider
157
+
158
+ def _load ( xml )
159
+ return @config = nil if ( @should[:_exist] == false )
160
+ admin = ''
161
+ if @should[:format].to_s == 'set'
162
+ @config = "\ndelete groups #{@name}\n" +
163
+ "edit groups #{@name}\n" +
164
+ File.read( @should[:path] )
165
+ admin = @should[:_active] == :false ? 'deactivate' : 'activate'
166
+ @config += "\nquit\n"
167
+ @config += "\n#{admin} groups #{@name}"
168
+
169
+ elsif @should[:format].to_s == 'text'
170
+ admin = @should[:_active] == :false ? 'inactive' : 'active'
171
+ admin += ": " unless admin.empty?
172
+ @config = "groups {\n#{admin} replace: #{@name} {\n" +
173
+ File.read( @should[:path] ) + "\n}\n}"
174
+
175
+ elsif @should[:format].to_s == 'xml'
176
+ xml.at_xpath('groups') << File.read( @should[:path])
177
+ @config = xml
178
+ end
179
+ return @config
180
+ end
181
+
182
+ def _apply_group
183
+ cfg = Netconf::JunosConfig.new(:TOP)
184
+ xml = cfg.doc
185
+ Nokogiri::XML::Builder.with( xml.at_xpath( 'configuration' )) do |dot|
186
+ if @config and @should[:_active] == :true
187
+ dot.send :'apply-groups', @name
188
+ else
189
+ dot.send :'apply-groups', @name, Netconf::JunosConfig::DELETE
190
+ end
191
+ end
192
+ begin
193
+ attr = {}
194
+ attr[:action] = 'replace'
195
+ attr[:format] = 'xml'
196
+ result = @ndev.rpc.load_configuration( xml, attr )
197
+ rescue Netconf::RpcError => e
198
+ errs = e.rsp.xpath('//rpc-error[error-severity = "error"]')
199
+ raise e unless errs.empty?
200
+ e.rsp
201
+ else
202
+ result
203
+ end
204
+ end
205
+ end
206
+
@@ -0,0 +1,30 @@
1
+
2
+ require "junos-ez/provider"
3
+
4
+ module Junos::Ez::IPports
5
+
6
+ PROPERTIES = [
7
+ :admin, # [:up, :down]
8
+ :description, # general description text
9
+ :tag_id, # VLAN tag-id for vlan-tag enabled ports
10
+ :mtu, # MTU value as number
11
+ :address, # ip/prefix as text, e.g. "192.168.10.22/24"
12
+ :acl_in, # input ACL name
13
+ :acl_out, # output ACL name
14
+ ]
15
+
16
+ def self.Provider( ndev, varsym )
17
+ newbie = Junos::Ez::IPports::Provider::CLASSIC.new( ndev )
18
+ newbie.properties = Junos::Ez::Provider::PROPERTIES + PROPERTIES
19
+ Junos::Ez::Provider.attach_instance_variable( ndev, varsym, newbie )
20
+ end
21
+
22
+ class Provider < Junos::Ez::Provider::Parent
23
+ # common parenting goes here ...
24
+ end
25
+
26
+ end
27
+
28
+ require 'junos-ez/ip_ports/classic'
29
+
30
+
@@ -0,0 +1,188 @@
1
+ class Junos::Ez::IPports::Provider::CLASSIC < Junos::Ez::IPports::Provider
2
+
3
+ ### ---------------------------------------------------------------
4
+ ### XML top placement
5
+ ### ---------------------------------------------------------------
6
+
7
+ def xml_at_top
8
+
9
+ # if just the IFD is given as the name, default to unit "0"
10
+ @ifd, @ifd_unit = @name.split '.'
11
+ @ifd_unit ||= "0"
12
+
13
+ Nokogiri::XML::Builder.new{ |x| x.configuration{
14
+ x.interfaces { x.interface { x.name @ifd
15
+ x.unit {
16
+ x.name @ifd_unit
17
+ return x
18
+ }
19
+ }}
20
+ }}
21
+ end
22
+
23
+ def xml_element_rename( new_name )
24
+
25
+ # if just the IFD is given as the name, default to unit "0"
26
+ n_ifd, n_ifl = new_name.split '.'
27
+ n_ifl ||= "0"
28
+
29
+ # do not allow rename to different IFD.
30
+ return false unless @ifd == n_ifd
31
+
32
+ # return the new element name
33
+ return n_ifl
34
+ end
35
+
36
+ ### ---------------------------------------------------------------
37
+ ### XML readers
38
+ ### ---------------------------------------------------------------
39
+
40
+ def xml_get_has_xml( xml )
41
+ xml.xpath('//unit')[0]
42
+ end
43
+
44
+ def xml_read_parser( as_xml, as_hash )
45
+ set_has_status( as_xml, as_hash )
46
+
47
+ as_hash[:admin] = as_xml.xpath('disable').empty? ? :up : :down
48
+ ifa_inet = as_xml.xpath('family/inet')
49
+
50
+ xml_when_item(as_xml.xpath('vlan-id')){ |i| as_hash[:tag_id] = i.text.to_i }
51
+ xml_when_item(as_xml.xpath('description')){ |i| as_hash[:description] = i.text }
52
+ xml_when_item(ifa_inet.xpath('mtu')){ |i| as_hash[:mtu] = i.text.to_i }
53
+
54
+ # The address could be a list.
55
+ address = []
56
+ ifa_inet.xpath('address').each do |addr|
57
+ address << addr.xpath('name').text
58
+ end
59
+ as_hash[:address] = address || nil
60
+
61
+ # check for firewall-filters (aka ACLs)
62
+ if (fw_acl = ifa_inet.xpath('filter')[0])
63
+ xml_when_item( fw_acl.xpath('input/filter-name')){ |i| as_hash[:acl_in] = i.text.strip }
64
+ xml_when_item( fw_acl.xpath('output/filter-name')){ |i| as_hash[:acl_out] = i.text.strip }
65
+ end
66
+
67
+ return true
68
+ end
69
+
70
+ ### ---------------------------------------------------------------
71
+ ### XML writers
72
+ ### ---------------------------------------------------------------
73
+
74
+ def xml_change_address( xml )
75
+ xml.family { xml.inet {
76
+ # delete the old address and replace it with the new one ...
77
+ if @has[:address]
78
+ xml.address( Netconf::JunosConfig::DELETE ) { xml.name @has[:address] }
79
+ end
80
+ xml.address { xml.name @should[:address] }
81
+ }}
82
+ end
83
+
84
+ def xml_change_tag_id( xml )
85
+ xml_set_or_delete( xml, 'vlan-id', @should[:tag_id] )
86
+ end
87
+
88
+ def xml_change_mtu( xml )
89
+ xml.family { xml.inet {
90
+ xml_set_or_delete( xml, 'mtu', @should[:mtu] )
91
+ }}
92
+ end
93
+
94
+ def xml_change_acl_in( xml )
95
+ xml.family { xml.inet { xml.filter { xml.input {
96
+ xml_set_or_delete( xml, 'filter-name', @should[:acl_in] )
97
+ }}}}
98
+ end
99
+
100
+ def xml_change_acl_out( xml )
101
+ xml.family { xml.inet { xml.filter { xml.output {
102
+ xml_set_or_delete( xml, 'filter-name', @should[:acl_out] )
103
+ }}}}
104
+ end
105
+
106
+ end
107
+
108
+ ##### ---------------------------------------------------------------
109
+ ##### Resource Methods
110
+ ##### ---------------------------------------------------------------
111
+
112
+ class Junos::Ez::IPports::Provider::CLASSIC
113
+ def status
114
+ got = @ndev.rpc.get_interface_information( :interface_name => @ifd+'.'+@ifd_unit )
115
+ ifs = got.xpath('logical-interface')[0]
116
+ ret_h = {}
117
+ ret_h[:l1_oper_status] = (ifs.xpath('if-config-flags/iff-device-down')[0]) ? :down : :up
118
+ ret_h[:oper_status] = (ifs.xpath('address-family//ifaf-down')[0]) ? :down : :up
119
+ ret_h[:snmp_index] = ifs.xpath('snmp-index').text.to_i
120
+ ret_h[:packets_rx] = ifs.xpath('traffic-statistics/input-packets').text.to_i
121
+ ret_h[:packets_tx] = ifs.xpath('traffic-statistics/output-packets').text.to_i
122
+ ret_h
123
+ end
124
+ end
125
+
126
+ ##### ---------------------------------------------------------------
127
+ ##### Provider collection methods
128
+ ##### ---------------------------------------------------------------
129
+
130
+ class Junos::Ez::IPports::Provider::CLASSIC
131
+
132
+ def build_list
133
+ from_junos_get_ifa_xml.collect do |ifa|
134
+ ifa.xpath('name').text.strip
135
+ end
136
+ end
137
+
138
+ def build_catalog
139
+ @catalog = {}
140
+
141
+ ## do the equivalent of "show interfaces ..." to retrieve the list
142
+ ## of known interfaces that have an IFA == 'inet'. Note that this
143
+ ## list will *not* include anything that has been deactivated.
144
+
145
+ ifa_list = from_junos_get_ifa_xml
146
+
147
+ ## from this list of IFA, retrieve the configurations
148
+
149
+ got_xml_cfg = @ndev.rpc.get_configuration do |cfg|
150
+ cfg.interfaces {
151
+ ifa_list.each do |ifa|
152
+ ifa_name = ifa.xpath('name').text.strip
153
+ ifa_ifd, ifa_ifl = ifa_name.split '.'
154
+ cfg.interface {
155
+ cfg.name ifa_ifd
156
+ cfg.unit { cfg.name ifa_ifl }
157
+ }
158
+ end
159
+ }
160
+ end
161
+
162
+ ## now create the object property hashes for each of the instances
163
+
164
+ got_xml_cfg.xpath('interfaces/interface/unit').each do |ifl|
165
+ ifd = ifl.xpath('preceding-sibling::name').text.strip
166
+ unit = ifl.xpath('name').text.strip
167
+ obj_name = ifd + '.' + unit
168
+
169
+ @catalog[obj_name] = {}
170
+ xml_read_parser( ifl, @catalog[obj_name] )
171
+ end
172
+
173
+ return @catalog
174
+ end
175
+
176
+ private
177
+
178
+ def from_junos_get_ifa_xml
179
+
180
+ xml_data = @ndev.rpc.get_interface_information(
181
+ :terse => true,
182
+ )
183
+
184
+ ifa_list = xml_data.xpath('interface-information/logical-interface[normalize-space(address-family/address-family-name) = "inet"]')
185
+
186
+ end
187
+
188
+ end
@@ -0,0 +1,121 @@
1
+ require "junos-ez/provider"
2
+
3
+ module Junos::Ez::L1ports
4
+
5
+ PROPERTIES = [
6
+ :admin, # [ :up, :down ]
7
+ :description, # string
8
+ :mtu, # number
9
+ :speed, # [ :auto, '10m', '100m', '1g', '10g' ]
10
+ :duplex, # [ :auto, :half, :full ]
11
+ :unit_count, # number of configured units
12
+ ]
13
+
14
+ IFS_NAME_FILTER = '[fgx]e-*'
15
+
16
+ def self.Provider( ndev, varsym )
17
+ newbie = case ndev.fact( :ifd_style )
18
+ when :SWITCH
19
+ Junos::Ez::L1ports::Provider::SWITCH.new( ndev )
20
+ when :CLASSIC
21
+ Junos::Ez::L1ports::Provider::CLASSIC.new( ndev )
22
+ end
23
+
24
+ newbie.properties = Junos::Ez::Provider::PROPERTIES + PROPERTIES
25
+ Junos::Ez::Provider.attach_instance_variable( ndev, varsym, newbie )
26
+ end
27
+
28
+ end
29
+
30
+ class Junos::Ez::L1ports::Provider < Junos::Ez::Provider::Parent
31
+
32
+ ### ---------------------------------------------------------------
33
+ ### XML readers
34
+ ### ---------------------------------------------------------------
35
+
36
+ def xml_get_has_xml( xml )
37
+ xml.xpath('//interface')[0]
38
+ end
39
+
40
+ def xml_change_mtu( xml )
41
+ xml_set_or_delete( xml, 'mtu', @should[:mtu] )
42
+ end
43
+
44
+ ### ---------------------------------------------------------------
45
+ ### Collection methods
46
+ ### ---------------------------------------------------------------
47
+
48
+ def build_list
49
+ @ndev.rpc.get_interface_information({
50
+ :media => true,
51
+ :terse => true,
52
+ :interface_name => Junos::Ez::L1ports::IFS_NAME_FILTER
53
+ }).xpath('physical-interface/name').collect do |ifs|
54
+ ifs.text.strip
55
+ end
56
+ end
57
+
58
+ def build_catalog
59
+ @catalog = {}
60
+
61
+ # we could have a large list of interfaces, so
62
+ # we need to break this up into individual "gets"
63
+
64
+ list!.each do |ifs_name|
65
+ @ndev.rpc.get_configuration{ |xml|
66
+ xml.interfaces {
67
+ xml.interface {
68
+ xml.name ifs_name
69
+ xml_read_filter( xml )
70
+ }
71
+ }
72
+ }.xpath('interfaces/interface').each do |ifs_xml|
73
+ @catalog[ifs_name] = {}
74
+ xml_read_parser( ifs_xml, @catalog[ifs_name] )
75
+ end
76
+ end
77
+
78
+ return @catalog
79
+ end
80
+
81
+ ### ---------------------------------------------------------------
82
+ ### Resource methods
83
+ ### ---------------------------------------------------------------
84
+
85
+ ## returns a Hash of status information, from "show interface ..."
86
+ ## basic information, not absolutely everything. but if a
87
+ ## block is given, then pass the XML to the block.
88
+
89
+ def status
90
+
91
+ got = @ndev.rpc.get_interface_information(:interface_name => @name, :media => true )
92
+ phy = got.xpath('physical-interface')[0]
93
+ return nil unless phy
94
+
95
+ ret_h = {}
96
+ ret_h[:macaddr] = phy.xpath('current-physical-address').text.strip
97
+ xml_when_item(phy.xpath('description')){|i| ret_h[:description] = i.text.strip }
98
+ ret_h[:oper_status] = phy.xpath('oper-status').text.strip
99
+ ret_h[:admin_status] = phy.xpath('admin-status').text.strip
100
+ ret_h[:mtu] = phy.xpath('mtu').text.to_i
101
+ ret_h[:speed] = {:admin => phy.xpath('speed').text.strip }
102
+ ret_h[:duplex] = {:admin => phy.xpath('duplex').text.strip }
103
+ ret_h[:autoneg] = phy.xpath('if-auto-negotiation').text.strip
104
+
105
+ if ret_h[:autoneg] == "enabled"
106
+ autoneg = phy.xpath('ethernet-autonegotiation')[0]
107
+ ret_h[:speed][:oper] = autoneg.xpath('link-partner-speed').text.strip
108
+ ret_h[:duplex][:oper] = autoneg.xpath('link-partner-duplexity').text.strip
109
+ end
110
+
111
+ # if a block is given, then it means the caller wants to process the XML data.
112
+ yield( phy ) if block_given?
113
+
114
+ ret_h
115
+ end
116
+
117
+ end
118
+
119
+ require 'junos-ez/l1_ports/switch'
120
+ require 'junos-ez/l1_ports/classic'
121
+