shopify-junos-ez-stdlib 1.0.0

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 (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
+