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,217 @@
1
+ =begin
2
+ =end
3
+
4
+ require 'junos-ez/system/userauths'
5
+
6
+ module Junos::Ez::Users
7
+
8
+ PROPERTIES = [
9
+ :uid, # User-ID, Number
10
+ :class, # User Class, String
11
+ :fullname, # Full Name, String
12
+ :password, # Encrypted password
13
+ :ssh_keys, # READ-ONLY, Hash of SSH public keys
14
+ ]
15
+
16
+ def self.Provider( ndev, varsym )
17
+ newbie = Junos::Ez::Users::Provider.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; end
23
+
24
+ end
25
+
26
+
27
+ ##### ---------------------------------------------------------------
28
+ ##### Provider Resource Methods
29
+ ##### ---------------------------------------------------------------
30
+
31
+ class Junos::Ez::Users::Provider
32
+
33
+ ### ---------------------------------------------------------------
34
+ ### XML top placement
35
+ ### ---------------------------------------------------------------
36
+
37
+ def xml_at_top
38
+ Nokogiri::XML::Builder.new{|x| x.configuration{
39
+ x.system { x.login { x.user {
40
+ x.name @name
41
+ return x
42
+ }}}}}
43
+ end
44
+
45
+ ### ---------------------------------------------------------------
46
+ ### XML readers
47
+ ### ---------------------------------------------------------------
48
+
49
+ def xml_get_has_xml( xml )
50
+ xml.xpath('//user')[0]
51
+ end
52
+
53
+ def xml_read_parser( as_xml, as_hash )
54
+ set_has_status( as_xml, as_hash )
55
+
56
+ as_hash[:uid] = as_xml.xpath('uid').text
57
+ as_hash[:class] = as_xml.xpath('class').text
58
+
59
+ xml_when_item(as_xml.xpath('full-name')) {|i|
60
+ as_hash[:fullname] = i.text
61
+ }
62
+
63
+ xml_when_item(as_xml.xpath('authentication/encrypted-password')) {|i|
64
+ as_hash[:password] = i.text
65
+ }
66
+
67
+ # READ-ONLY capture the keys
68
+ unless (keys = as_xml.xpath('authentication/ssh-rsa')).empty?
69
+ as_hash[:ssh_keys] ||= {}
70
+ as_hash[:ssh_keys]['ssh-rsa'] = keys.collect{|key| key.text.strip}
71
+ end
72
+ unless (keys = as_xml.xpath('authentication/ssh-dsa')).empty?
73
+ as_hash[:ssh_keys] ||= {}
74
+ as_hash[:ssh_keys]['ssh-dsa'] = keys.collect{|key| key.text.strip}
75
+ end
76
+ end
77
+
78
+ ### ---------------------------------------------------------------
79
+ ### XML writers
80
+ ### ---------------------------------------------------------------
81
+
82
+ def xml_change_password( xml )
83
+ xml.authentication {
84
+ xml_set_or_delete( xml, 'encrypted-password', @should[:password] )
85
+ }
86
+ end
87
+
88
+ def xml_change_fullname( xml )
89
+ xml_set_or_delete( xml, 'full-name', @should[:fullname] )
90
+ end
91
+
92
+ # changing the 'gid' is changing the Junos 'class' element
93
+ # so, what is tough here is that the Nokogiri Builder mech
94
+ # won't allow us to use the string 'class' since it conflicts
95
+ # with the Ruby language. So we need to add the 'class' element
96
+ # the hard way, yo! ...
97
+
98
+ def xml_change_class( xml )
99
+ par = xml.instance_variable_get(:@parent)
100
+ doc = xml.instance_variable_get(:@doc)
101
+ user_class = Nokogiri::XML::Node.new('class', doc )
102
+ user_class.content = @should[:class]
103
+ par.add_child( user_class )
104
+ end
105
+
106
+ def xml_change_uid( xml )
107
+ xml_set_or_delete( xml, 'uid', @should[:uid] )
108
+ end
109
+
110
+ end
111
+
112
+ ##### ---------------------------------------------------------------
113
+ ##### Provider Collection Methods
114
+ ##### ---------------------------------------------------------------
115
+
116
+ class Junos::Ez::Users::Provider
117
+
118
+ def build_list
119
+ @ndev.rpc.get_configuration{ |x| x.system {
120
+ x.login {
121
+ x.user({:recurse => 'false'})
122
+ }
123
+ }}
124
+ .xpath('//user/name').collect{ |i| i.text }
125
+ end
126
+
127
+ def build_catalog
128
+ @catalog = {}
129
+ @ndev.rpc.get_configuration{ |x| x.system {
130
+ x.login
131
+ }}
132
+ .xpath('//user').each do |user|
133
+ name = user.xpath('name').text
134
+ @catalog[name] = {}
135
+ xml_read_parser( user, @catalog[name] )
136
+ end
137
+ @catalog
138
+ end
139
+
140
+ end
141
+
142
+ ##### ---------------------------------------------------------------
143
+ ##### Resource Methods
144
+ ##### ---------------------------------------------------------------
145
+
146
+ class Junos::Ez::Users::Provider
147
+
148
+ ## ----------------------------------------------------------------
149
+ ## change the password by providing it in plain-text
150
+ ## ----------------------------------------------------------------
151
+
152
+ def password=(plain_text)
153
+ xml = xml_at_top
154
+ xml.authentication {
155
+ xml.send(:'plain-text-password-value', plain_text)
156
+ }
157
+ @ndev.rpc.load_configuration( xml )
158
+ return true
159
+ end
160
+
161
+ ## ----------------------------------------------------------------
162
+ ## get a Hash that is used as the 'name' for obtaining a resource
163
+ ## for Junos::Ez::UserAuths::Provider
164
+ ## ----------------------------------------------------------------
165
+
166
+ def ssh_key( keytype, index = 0 )
167
+ return nil unless @has[:ssh_keys]
168
+ return nil unless @has[:ssh_keys][keytype]
169
+ ret_h = {:user => @name, :keytype => keytype}
170
+ ret_h[:publickey] = @has[:ssh_keys][keytype][index]
171
+ ret_h
172
+ end
173
+
174
+ ##
175
+ ## @@ need to move this code into the main provider
176
+ ## @@ as a utility ...
177
+ ##
178
+
179
+ def get_userauth_provd
180
+ @ndev.providers.each do |p|
181
+ obj = @ndev.send(p)
182
+ return obj if obj.class == Junos::Ez::UserAuths::Provider
183
+ end
184
+ end
185
+
186
+ ## ----------------------------------------------------------------
187
+ ## load an SSH public key & return the resulting key object.
188
+ ## You can provide the publickey either as :publickey or
189
+ ## contents will be read from :filename
190
+ ## ----------------------------------------------------------------
191
+
192
+ def load_ssh_key!( opts = {} )
193
+ publickey = opts[:publickey] || File.read( opts[:filename] ).strip
194
+ raise ArgumentError, "no public-key specified" unless publickey
195
+
196
+ # nab the provider for handling ssh-keys, since we'll use that
197
+ # for key resource management
198
+
199
+ @auth_provd ||= get_userauth_provd
200
+ raise StandardError, "No Junos::Ez::UserAuths::Provider" unless @auth_provd
201
+
202
+ # extract the key-type from the public key.
203
+ keytype = publickey[0..6]
204
+ keytype = 'ssh-dsa' if keytype == 'ssh-dss'
205
+ raise ArgumentError, "Unknown ssh key-type #{keytype}" unless Junos::Ez::UserAuths::VALID_KEY_TYPES.include? keytype
206
+
207
+ # ok, we've got everything we need to add the key, so here we go.
208
+ key_name = {:user => @name, :keytype => keytype, :publickey => publickey }
209
+ key = @auth_provd[ key_name ]
210
+ key.write!
211
+
212
+ # return the key in case the caller wants it
213
+ key
214
+ end
215
+
216
+ end
217
+
@@ -0,0 +1,236 @@
1
+ =begin
2
+ ---------------------------------------------------------------------
3
+ Config::Utils is a collection of methods used for loading
4
+ configuration files/templates and software images
5
+
6
+ commit! - commit configuration
7
+ commit? - see if a candidate config is OK (commit-check)
8
+ diff? - shows the diff of the candidate config w/current | rolback
9
+ load! - load configuration onto device
10
+ lock! - take exclusive lock on config
11
+ unlock! - release exclusive lock on config
12
+ rollback! - perform a config rollback
13
+ get_config - returns requested config in "text" format-style
14
+
15
+ ---------------------------------------------------------------------
16
+ =end
17
+
18
+ module Junos::Ez::Config
19
+ def self.Utils( ndev, varsym )
20
+ newbie = Junos::Ez::Config::Provider.new( ndev )
21
+ Junos::Ez::Provider.attach_instance_variable( ndev, varsym, newbie )
22
+ end
23
+ end
24
+
25
+ ### -----------------------------------------------------------------
26
+ ### PUBLIC METHODS
27
+ ### -----------------------------------------------------------------
28
+ ### -----------------------------------------------------------------
29
+
30
+ class Junos::Ez::Config::Provider < Junos::Ez::Provider::Parent
31
+
32
+ ### ---------------------------------------------------------------
33
+ ### load! - used to load configuration files / templates. This
34
+ ### does not perform a 'commit', just the equivalent of the
35
+ ### load-configuration RPC
36
+ ###
37
+ ### --- options ---
38
+ ###
39
+ ### :filename => path - indcates the filename of content
40
+ ### note: filename extension will also define format
41
+ ### .{conf,text,txt} <==> :text
42
+ ### .xml <==> :xml
43
+ ### .set <==> :set
44
+ ###
45
+ ### :content => String - string content of data (vs. :filename)
46
+ ###
47
+ ### :format => [:text, :set, :xml], default :text (curly-brace)
48
+ ### this will override any auto-format from the :filename
49
+ ###
50
+ ### :binding - indicates file/content is an ERB
51
+ ### => <object> - will grab the binding from this object
52
+ ### using a bit of meta-programming magic
53
+ ### => <binding> - will use this binding
54
+ ###
55
+ ### :replace! => true - enables the 'replace' option
56
+ ### :overwrite! => true - enables the 'overwrite' optoin
57
+ ###
58
+ ### --- returns ---
59
+ ### true if the configuration is loaded OK
60
+ ### raise Netconf::EditError otherwise
61
+ ### ---------------------------------------------------------------
62
+
63
+ def load!( opts = {} )
64
+ raise ArgumentError unless opts[:content] || opts[:filename]
65
+
66
+ content = opts[:content] || File.read( opts[:filename] )
67
+
68
+ attrs = {}
69
+ attrs[:action] = 'replace' if opts[:replace!]
70
+ attrs[:action] = 'override' if opts[:override!]
71
+
72
+ if opts[:format]
73
+ attrs[:format] = opts[:format].to_s
74
+ elsif opts[:filename]
75
+ case f_ext = File.extname( opts[:filename] )
76
+ when '.conf','.text','.txt'; attrs[:format] = 'text'
77
+ when '.set'; attrs[:format] = 'set'
78
+ when '.xml'; # default is XML
79
+ else
80
+ raise ArgumentError, "unknown format from extension: #{f_ext}"
81
+ end
82
+ else
83
+ raise ArgumentError "unspecified format"
84
+ end
85
+
86
+ if opts[:binding]
87
+ erb = ERB.new( content, nil, '>' )
88
+ case opts[:binding]
89
+ when Binding
90
+ # binding was provided to use
91
+ content = erb.result( opts[:binding] )
92
+ when Object
93
+ obj = opts[:binding]
94
+ def obj.junos_ez_binding; binding end
95
+ content = erb.result( obj.junos_ez_binding )
96
+ class << obj; remove_method :junos_ez_binding end
97
+ end
98
+ end
99
+
100
+ @ndev.rpc.load_configuration( content, attrs )
101
+ true # everthing OK!
102
+ end
103
+
104
+ ### ---------------------------------------------------------------
105
+ ### commit! - commits the configuration to the device
106
+ ###
107
+ ### --- options ---
108
+ ###
109
+ ### :confirm => true | timeout
110
+ ### :comment => commit log comment
111
+ ###
112
+ ### --- returns ---
113
+ ### true if commit completed
114
+ ### raises Netconf::CommitError otherwise
115
+ ### ---------------------------------------------------------------
116
+
117
+ def commit!( opts = {} )
118
+
119
+ args = {}
120
+ args[:log] = opts[:comment] if opts[:comment]
121
+ if opts[:confirm]
122
+ args[:confirmed] = true
123
+ if opts[:confirm] != true
124
+ timeout = Integer( opts[:confirm] ) rescue false
125
+ raise ArgumentError "invalid timeout #{opts[:confirm]}" unless timeout
126
+ args[:confirm_timeout] = timeout
127
+ end
128
+ end
129
+
130
+ @ndev.rpc.commit_configuration( args )
131
+ true
132
+ end
133
+
134
+ ### ---------------------------------------------------------------
135
+ ### commit? - perform commit configuration check
136
+ ###
137
+ ### --- returns ---
138
+ ### true if candidate config is OK to commit
139
+ ### Array of rpc-error data otherwise
140
+ ### ---------------------------------------------------------------
141
+
142
+ def commit?
143
+ begin
144
+ @ndev.rpc.commit_configuration( :check => true )
145
+ rescue => e
146
+ return Junos::Ez::rpc_errors( e.rsp )
147
+ end
148
+ true # commit check OK!
149
+ end
150
+
151
+ ### ---------------------------------------------------------------
152
+ ### rollback! - used to rollback the configuration
153
+ ### ---------------------------------------------------------------
154
+
155
+ def rollback!( rollback_id = 0 )
156
+ raise ArgumentError, "invalid rollback #{rollback_id}" unless ( rollback_id >= 0 and rollback_id <= 50 )
157
+ @ndev.rpc.load_configuration( :compare=>'rollback', :rollback=> rollback_id.to_s )
158
+ true # rollback OK!
159
+ end
160
+
161
+ ### ---------------------------------------------------------------
162
+ ### diff? - displays diff (patch format) between
163
+ ### current candidate configuration loaded and the rollback_id
164
+ ###
165
+ ### --- returns ---
166
+ ### nil if no diff
167
+ ### String of diff output otherwise
168
+ ### ---------------------------------------------------------------
169
+
170
+ def diff?( rollback_id = 0 )
171
+ raise ArgumentError, "invalid rollback #{rollback_id}" unless ( rollback_id >= 0 and rollback_id <= 50 )
172
+ got = ndev.rpc.get_configuration( :compare=>'rollback', :rollback=> rollback_id.to_s )
173
+ diff = got.xpath('configuration-output').text
174
+ return nil if diff == "\n"
175
+ diff
176
+ end
177
+
178
+ ### ---------------------------------------------------------------
179
+ ### lock! - takes an exclusive lock on the candidate config
180
+ ###
181
+ ### --- returns ---
182
+ ### true if lock acquired
183
+ ### raise Netconf::LockError otherwise
184
+ ### ---------------------------------------------------------------
185
+
186
+ def lock!
187
+ @ndev.rpc.lock_configuration
188
+ true
189
+ end
190
+
191
+ ### ---------------------------------------------------------------
192
+ ### unlock! - releases exclusive lock on candidate config
193
+ ###
194
+ ### --- returns ---
195
+ ### true if lock release
196
+ ### raise Netconf::RpcError otherwise
197
+ ### ---------------------------------------------------------------
198
+
199
+ def unlock!
200
+ @ndev.rpc.unlock_configuration
201
+ true
202
+ end
203
+
204
+ ### ---------------------------------------------------------------
205
+ ### get_config - returns String of requested (or entire) config
206
+ ### in "text" (curly-brace) format. The 'rqst' argument
207
+ ### identifies the scope of the config, for example:
208
+ ###
209
+ ### .get_config( "interfaces ge-0/0/0" )
210
+ ###
211
+ ### If there is no configuration available, 'nil' is returned
212
+ ###
213
+ ### If there is an error in the request, that will be returned
214
+ ### as a String with "ERROR!" prepended
215
+ ### ---------------------------------------------------------------
216
+
217
+ def get_config( rqst = nil )
218
+ scope = "show configuration"
219
+ scope.concat( " " + rqst ) if rqst
220
+ begin
221
+ @ndev.rpc.command( scope, :format => 'text' ).xpath('configuration-output').text
222
+ rescue NoMethodError
223
+ # indicates no configuration found
224
+ nil
225
+ rescue => e
226
+ # indicates error in request
227
+ err = e.rsp.xpath('rpc-error')[0]
228
+ err_info = err.xpath('error-info/bad-element').text
229
+ err_msg = err.xpath('error-message').text
230
+ "ERROR! " + err_msg + ": " + err_info
231
+ end
232
+ end
233
+
234
+ end # class Provider
235
+
236
+