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