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
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
LICENSE (BSD-2)
|
2
|
+
===============
|
3
|
+
Copyright (c) 2013, Jeremy Schulman, Juniper Networks
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
8
|
+
|
9
|
+
Redistributions of source code must retain the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer.
|
11
|
+
|
12
|
+
Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
this list of conditions and the following disclaimer in
|
14
|
+
the documentation and/or other materials provided with the distribution.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
17
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
18
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
19
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
20
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
21
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
22
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
23
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
24
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
25
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
26
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# OVERVIEW
|
2
|
+
|
3
|
+
Ruby framework to support Junos OS based device management automation.
|
4
|
+
|
5
|
+
This is the "standard library" or "core" set of functionality that should work on most/all Junos OS based devices.
|
6
|
+
|
7
|
+
This framework is build on top of the NETCONF gem which uses XML as the fundamental data-exchange. So no
|
8
|
+
"automating the CLI" or using SNMP. The purpose of this framework is to **enable automation development
|
9
|
+
without requiring specific Junos XML knowledge**.
|
10
|
+
|
11
|
+
Further documentation can be found in the *docs* subdirectory.
|
12
|
+
|
13
|
+
# FRAMEWORK
|
14
|
+
|
15
|
+
The framework is comprised of these basic eloements:
|
16
|
+
|
17
|
+
- Facts:
|
18
|
+
|
19
|
+
A Hash of name/value pairs of information auto-collected. Fact values can be Hash structures as well
|
20
|
+
so you can have deeply nested fact data. You can also define your own facts in addition to the "stdlib" facts.
|
21
|
+
The facts are used by the framework to create a platform indepent layer of abstraction. This means
|
22
|
+
that managing a VLAN, for example, is the same regardless of the underlying hardware platofrm (EX, QFX,
|
23
|
+
MX, SRX, ...)
|
24
|
+
|
25
|
+
- Resources:
|
26
|
+
|
27
|
+
Resources allow you to easily configure and perform operational functions on specific items within Junos,
|
28
|
+
for example VLANs, or switch ports. A resource has *properties* that you manipuate as Hash. You can
|
29
|
+
interact with Junos using resource methods like `read!`, `write!`, `delete!`, `activate!`, `deactivate!`, etc.
|
30
|
+
For a complete listing of resource methods, refer to the *docs* directory
|
31
|
+
|
32
|
+
- Providers:
|
33
|
+
|
34
|
+
Providers allow you to manage a collection of resource, and most commonly, select a resource.
|
35
|
+
The purpose of a provider/resource is to automate the life-cycle of common changes, like adding
|
36
|
+
VLANs, or ports to a VLAN. A provider also allows you to obtain a `list` of resources
|
37
|
+
(Array of *names*) or a `catalog` (Hash of resource properties). Providers may include resource
|
38
|
+
specific functionality, like using complex YAML/Hash data for easy import/export and provisioning
|
39
|
+
with Junos. If you need the ability to simply apply config-snippets that you do not need to model
|
40
|
+
as resources (as you might for initial device commissioning), the Utilities library is where you
|
41
|
+
want to start.
|
42
|
+
|
43
|
+
- Utilities:
|
44
|
+
|
45
|
+
Utilities are simply collections of functions. The **configuration** utilities, for example, will
|
46
|
+
allow you to easily push config snippets in "curly-brace", "set", or XML formats. Very useful
|
47
|
+
for unmanaged provider/resources (like initial configuration of the device). The
|
48
|
+
**routing-engine** utilities, for example, will allow you to easily upgrade software, check
|
49
|
+
memory usage, and do `ping` operations.
|
50
|
+
|
51
|
+
# EXAMPLE USAGE
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'pp'
|
55
|
+
require 'net/netconf/jnpr'
|
56
|
+
require 'junos-ez/stdlib'
|
57
|
+
|
58
|
+
unless ARGV[0]
|
59
|
+
puts "You must specify a target"
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
|
63
|
+
# login information for NETCONF session
|
64
|
+
login = { :target => ARGV[0], :username => 'jeremy', :password => 'jeremy1', }
|
65
|
+
|
66
|
+
## create a NETCONF object to manage the device and open the connection ...
|
67
|
+
|
68
|
+
ndev = Netconf::SSH.new( login )
|
69
|
+
print "Connecting to device #{login[:target]} ... "
|
70
|
+
ndev.open
|
71
|
+
puts "OK!"
|
72
|
+
|
73
|
+
## Now bind providers to the device object. The 'Junos::Ez::Provider' must be first.
|
74
|
+
## This will retrieve the device 'facts'. The other providers allow you to define the
|
75
|
+
## provider variables; so this example is using 'l1_ports' and 'ip_ports', but you could name
|
76
|
+
## them what you like, yo!
|
77
|
+
|
78
|
+
Junos::Ez::Provider( ndev )
|
79
|
+
Junos::Ez::L1ports::Provider( ndev, :l1_ports )
|
80
|
+
Junos::Ez::IPports::Provider( ndev, :ip_ports )
|
81
|
+
Junoz::Ez::Config::Utils( ndev, :cu )
|
82
|
+
|
83
|
+
# -----------------------------------------------------------
|
84
|
+
# Facts ...
|
85
|
+
# -----------------------------------------------------------
|
86
|
+
|
87
|
+
# show the device softare version fact
|
88
|
+
pp ndev.fact :version
|
89
|
+
|
90
|
+
# show the device serial-number face
|
91
|
+
pp ndev.fact :serialnumber
|
92
|
+
|
93
|
+
# get a list of all available facts (Array)
|
94
|
+
pp ndev.facts.list
|
95
|
+
|
96
|
+
# get a hash of all facts and their associated values
|
97
|
+
pp ndev.facts.catalog
|
98
|
+
|
99
|
+
# -----------------------------------------------------------
|
100
|
+
# Layer 1 (physical ports) Resources ...
|
101
|
+
# -----------------------------------------------------------
|
102
|
+
|
103
|
+
pp ndev.l1_ports.list
|
104
|
+
pp ndev.l1_ports.catalog
|
105
|
+
|
106
|
+
# select port 'ge-0/0/0' and display the contents
|
107
|
+
# of the properties (like port, speed, description)
|
108
|
+
|
109
|
+
ge_0 = ndev.l1_ports['ge-0/0/0']
|
110
|
+
pp ge_0.to_h
|
111
|
+
|
112
|
+
# change port to disable, this will write the change
|
113
|
+
# but not commit it.
|
114
|
+
|
115
|
+
ge_0[:admin] = :down
|
116
|
+
ge_0.write!
|
117
|
+
|
118
|
+
# show the diff of the change to the screen
|
119
|
+
|
120
|
+
puts ndev.cu.diff?
|
121
|
+
|
122
|
+
# now rollback the change, since we don't want to save it.
|
123
|
+
|
124
|
+
ndev.cu.rollback!
|
125
|
+
|
126
|
+
ndev.close
|
127
|
+
```
|
128
|
+
|
129
|
+
# PROVIDERS
|
130
|
+
|
131
|
+
Providers manage access to individual resources and their associated properties. Providers/resources exists
|
132
|
+
for managing life-cycle common changes that you generally need as part of a larger workflow process. For more
|
133
|
+
documentation on Providers/Resources, see the *docs* directory.
|
134
|
+
|
135
|
+
- L1ports: Physical port management
|
136
|
+
- L2ports: Ethernet port (VLAN) management
|
137
|
+
- Vlans: VLAN resource management
|
138
|
+
- IPports: IP v4 port management
|
139
|
+
- StaticHosts: Static Hosts [system static-host-mapping ...]
|
140
|
+
- StaticRoutes: Static Routes [routing-options static ...]
|
141
|
+
|
142
|
+
# UTILITIES
|
143
|
+
|
144
|
+
- Config:
|
145
|
+
|
146
|
+
These functions allow you to load config snippets, do commit checks, look at config diffs, etc.
|
147
|
+
Generally speaking, you would want to use the Providers/Resources framework to manage specific
|
148
|
+
items in the config. This utility library is very useful when doing the initial commissioning
|
149
|
+
process, where you do not (cannot) model every aspect of Junos. These utilities can also be
|
150
|
+
used in conjunction with Providers/Resources, specifically around locking/unlocking and committing
|
151
|
+
the configuration.
|
152
|
+
|
153
|
+
- Filesystem:
|
154
|
+
|
155
|
+
These functions provide you "unix-like" commands that return data in Hash forms rather than
|
156
|
+
as string output you'd normally have to screen-scraps. These methods include `ls`, `df`, `pwd`,
|
157
|
+
`cwd`, `cleanup`, and `cleanup!`
|
158
|
+
|
159
|
+
- Routing-Engine:
|
160
|
+
|
161
|
+
These functions provide a general collection to information and functioanlity for handling
|
162
|
+
routing-engine (RE) processes. These functions `reboot!`, `shutdown!`, `install_software!`,
|
163
|
+
`ping`. Information gathering such as memory-usage, current users, and RE status information
|
164
|
+
is also made available through this collection.
|
165
|
+
|
166
|
+
# DEPENDENCIES
|
167
|
+
|
168
|
+
* gem netconf
|
169
|
+
* Junos OS based products
|
170
|
+
|
171
|
+
# INSTALLATION
|
172
|
+
|
173
|
+
* gem install junos-ez-stdlib
|
174
|
+
|
175
|
+
# CONTRIBUTORS
|
176
|
+
|
177
|
+
* Jeremy Schulman, @nwkautomaniac
|
178
|
+
|
179
|
+
# LICENSES
|
180
|
+
|
181
|
+
BSD-2, See LICENSE file
|
data/docs/Facts.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Fact Keeping
|
2
|
+
|
3
|
+
This framework is *fact based*, meaning that the provider libraries will have access to information about each
|
4
|
+
target. Facts enable the framework to abstract the physical differences of the underlying hardware.
|
5
|
+
|
6
|
+
For example,the `Junos::Ez::Vlans::Provider` allows you to manage vlans without having to worry about the differences between the EX product family and the MX product family. To you, the programmer, you simply obtain a resource and manage the associated properties.
|
7
|
+
|
8
|
+
There are collection of standard facts that are always read by the framework. You can find a list of these and the assocaited code in the *libs/../facts* subdirectory. These facts are also avaialble to your program as well. So you can make programmatic decisions based on the facts of the device.
|
9
|
+
|
10
|
+
You can also define your own facts, and then go on to building your own provider libraries (but we're getting ahead of ourselfs here ...)
|
11
|
+
|
12
|
+
# Usage
|
13
|
+
|
14
|
+
Usage rule: you **MUST** call `Junos::Ez::Provider` on your netconf object:
|
15
|
+
|
16
|
+
- **AFTER** the object has connected to the target, since it will read facts
|
17
|
+
|
18
|
+
- **BEFORE** you add any other providers, since these may use the facts
|
19
|
+
|
20
|
+
Here is a basic example:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'pp'
|
24
|
+
require 'net/netconf/jnpr'
|
25
|
+
require 'junos-ez/stdlib'
|
26
|
+
|
27
|
+
login = { :target => ARGV[0], :username => 'jeremy', :password => 'jeremy1', }
|
28
|
+
|
29
|
+
# create a NETCONF object to manage the device
|
30
|
+
|
31
|
+
ndev = Netconf::SSH.new( login )
|
32
|
+
|
33
|
+
# open the NETCONF connetion, if this fails, the object will throw an exception
|
34
|
+
ndev.open
|
35
|
+
|
36
|
+
# now that the netconf session has been established, initialize the object for the
|
37
|
+
# Junos::Ez framework. This will add an instance variable called `@facts` and
|
38
|
+
# retrieve all known facts from the target
|
39
|
+
|
40
|
+
Junos::Ez::Provider( ndev )
|
41
|
+
|
42
|
+
# do a quick dump of all facts
|
43
|
+
|
44
|
+
pp ndev.facts.catalog
|
45
|
+
|
46
|
+
-> {:hardwaremodel=>"SRX210H",
|
47
|
+
:serialnumber=>"AD2909AA0096",
|
48
|
+
:hostname=>"srx210",
|
49
|
+
:domain=>"",
|
50
|
+
:fqdn=>"srx210",
|
51
|
+
:RE=>
|
52
|
+
{:status=>"OK",
|
53
|
+
:model=>"RE-SRX210H",
|
54
|
+
:up_time=>"26 days, 15 hours, 46 minutes, 4 seconds",
|
55
|
+
:last_reboot_reason=>"0x200:normal shutdown"},
|
56
|
+
:personality=>:SRX_BRANCH,
|
57
|
+
:ifd_style=>:CLASSIC,
|
58
|
+
:switch_style=>:VLAN,
|
59
|
+
:version=>"12.1X44-D10.4"}
|
60
|
+
```
|
61
|
+
|
62
|
+
# Methods
|
63
|
+
|
64
|
+
- `read!` - reloads the facts from the target
|
65
|
+
- `[]` - retrieve a specific fact from the keeper
|
66
|
+
- `fact` - alternative method to retrieve a specific fact from the keeper
|
67
|
+
- `list`, `list!` - returns an Array of fact names (symbols)
|
68
|
+
- `catalog`, `catalog!` - returns a Hash of fact names and values
|
69
|
+
|
70
|
+
The bang (!) indicates that the method will re-read the value from the target, which the non-bang method uses the values cached in memory. If the cache does not exist, the framework will read the values. The use of the bang-methods are handy if/when you have facts whose values change at runtime, like the `ndev.fact(:RE)[:up_time]`
|
71
|
+
|
72
|
+
# Definig Custom Facts
|
73
|
+
|
74
|
+
You can define your own facts using `Junos::Ez::Facts::Keeper.define`. You can review the stdlib facts by looking the *libs/../facts* subdirectory of this repo. Here is the code for the `:chassis` fact. What is interesting about this example, is this code will actually create multiple facts about the chassis. So there is not a required one-to-one relationship between createing a custom fact and the actual number of facts it creates, yo!
|
75
|
+
|
76
|
+
When you define your fact, you must give it a unique "fact name*, and a block. The block takee two arguments: the first is the netconf object, which will provide you access to the underlying Junos XML netconf, an a Hash that allows you to write your facts into the Keeper:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
Junos::Ez::Facts::Keeper.define( :chassis ) do |ndev, facts|
|
80
|
+
|
81
|
+
inv_info = ndev.rpc.get_chassis_inventory
|
82
|
+
chassis = inv_info.xpath('chassis')
|
83
|
+
|
84
|
+
facts[:hardwaremodel] = chassis.xpath('description').text
|
85
|
+
facts[:serialnumber] = chassis.xpath('serial-number').text
|
86
|
+
|
87
|
+
cfg = ndev.rpc.get_configuration{|xml|
|
88
|
+
xml.system {
|
89
|
+
xml.send(:'host-name')
|
90
|
+
xml.send(:'domain-name')
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
facts[:hostname] = cfg.xpath('//host-name').text
|
95
|
+
facts[:domain] = cfg.xpath('//domain-name').text
|
96
|
+
facts[:fqdn] = facts[:hostname]
|
97
|
+
facts[:fqdn] += ".#{facts[:domain]}" unless facts[:domain].empty?
|
98
|
+
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
data/docs/IPports.md
ADDED
data/docs/L1ports.md
ADDED
data/docs/L2ports.md
ADDED
@@ -0,0 +1,304 @@
|
|
1
|
+
# RESOURCES
|
2
|
+
|
3
|
+
Let's start with Resources before we get to Providers. Resources are generally where all "the action" happens.
|
4
|
+
The primary purpose of a resource is to allow you to make configuration changes without having to know
|
5
|
+
the underlying Junos XML configuration. When you have a resource, you can always get a list of the properties
|
6
|
+
available to you.
|
7
|
+
|
8
|
+
Here's an example of looking at a an ethernet switching port using the L2ports provider.
|
9
|
+
This is just a snippet of code, and they use the (->) notation to indicate standard output results.
|
10
|
+
|
11
|
+
For the following example assume that `l2_ports` is the provider assigned to the `ndev` object. The `ndev` object is of class `Netconf::SSH` (or has this as a parent class)
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
|
15
|
+
# select a port by name from the provider
|
16
|
+
|
17
|
+
port = ndev.l2_ports['ge-0/0/0']
|
18
|
+
|
19
|
+
# check to see if this actually exists in the config
|
20
|
+
|
21
|
+
unless port.exists?
|
22
|
+
puts "This port #{port.name} does not exist!"
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
|
26
|
+
# now pretty-print the properties associated with this resource (which would be the same list for
|
27
|
+
# any L2port resource
|
28
|
+
|
29
|
+
pp port.properties
|
30
|
+
|
31
|
+
-> [:_exist, :_active, :description, :untagged_vlan, :tagged_vlans, :vlan_tagging]
|
32
|
+
|
33
|
+
# now look at the specific values for this resource by pp the assocaite hash
|
34
|
+
|
35
|
+
pp port.to_h
|
36
|
+
|
37
|
+
-> {"ge-0/0/0"=>
|
38
|
+
{:_active=>true,
|
39
|
+
:_exist=>true,
|
40
|
+
:vlan_tagging=>true,
|
41
|
+
:tagged_vlans=>["Red", "Green", "Blue"],
|
42
|
+
:untagged_vlan=>nil}}
|
43
|
+
```
|
44
|
+
|
45
|
+
## Resource Methods
|
46
|
+
|
47
|
+
- `read!` - reads the contents into the resource read-hash (@has)
|
48
|
+
- `write!` - writes the contents from the resource write-hash (@should) to the device
|
49
|
+
- `delete!` - delete the resource from the configuration
|
50
|
+
- `activate!` - activates the configuration for this resource
|
51
|
+
- `deactivate!` - deactivates the configuration for this resource
|
52
|
+
- `rename!` - renames the resource in the configuration
|
53
|
+
- `reorder!` - reorders (Junos `insert`) the resource in the configuration
|
54
|
+
- `exists?` - indicates if this resource exists in the configuration
|
55
|
+
- `active?` - indicates if this resource is active in the configuration
|
56
|
+
- `to_h` - return the read-from and write-to hash values
|
57
|
+
|
58
|
+
## Instance Variables
|
59
|
+
|
60
|
+
Each resource include a hash structure containing the values read-from the device. This is
|
61
|
+
the `@has` variable. When modifying properties, the changed values are stored in the
|
62
|
+
write-to hash `@should` variable. These instance variable are made accessible, but you
|
63
|
+
should not access them directly. See next section for changing the property values.
|
64
|
+
|
65
|
+
## Reading Properties
|
66
|
+
|
67
|
+
You can obtain the entire `@has` property hash has using the `to_h` method. This example selects the "ge-0/0/0" physical port and dumps the property hash:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
port = ndev.l1_ports["ge-0/0/1"]
|
71
|
+
|
72
|
+
pp port.to_h
|
73
|
+
|
74
|
+
-> {"ge-0/0/1"=>
|
75
|
+
{:_active=>true,
|
76
|
+
:_exist=>true,
|
77
|
+
:admin=>:up,
|
78
|
+
:duplex=>:auto,
|
79
|
+
:speed=>:auto,
|
80
|
+
:unit_count=>26}}
|
81
|
+
|
82
|
+
```
|
83
|
+
|
84
|
+
You can also obtain just a specific property using the `[]` operator:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
pp port[:admin]
|
88
|
+
|
89
|
+
-> :up
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
## Modifying Properties
|
95
|
+
|
96
|
+
Modifying the resource property is simply making use of the `[]=` operator. For example,
|
97
|
+
setting the `:untagged_vlan` to "Black" and writing that back to the device would
|
98
|
+
look something like this:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
port[:untagged_vlan] = "Black"
|
102
|
+
port.write!
|
103
|
+
```
|
104
|
+
|
105
|
+
You can also obtain the `@should` property hash using the `to_h` method and providing the optional `:write` argument:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
port[:admin] = :down
|
109
|
+
|
110
|
+
pp port.to_h( :write )
|
111
|
+
|
112
|
+
-> {"ge-0/0/1"=>{:admin=>:down}}
|
113
|
+
```
|
114
|
+
|
115
|
+
_NOTE: The `@should` property hash only contains the changes that will be applied, not every property value._
|
116
|
+
|
117
|
+
When you execute the `write!` method, the framework will examine the contents of
|
118
|
+
the `@should` hash to determine what changes need to be made. On success the
|
119
|
+
values are then transfered into the `@has` hash.
|
120
|
+
|
121
|
+
If you're going to make changes to an array property, you would need to do something like this:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
port[:tagged_vlans] += ["Purple"]
|
125
|
+
port[:tagged_vlans] -= ["Red", "Green"]
|
126
|
+
port.write!
|
127
|
+
```
|
128
|
+
|
129
|
+
_NOTE: for Array values, do not use array methods like `delete` as they will operate
|
130
|
+
on the `@has` hash._
|
131
|
+
|
132
|
+
## Special Properties
|
133
|
+
|
134
|
+
All resources include two special properties `_exist` and `_active`. These control whether or
|
135
|
+
not a resource exists (or should) and whether or not the resource is active (or deactive).
|
136
|
+
Generally speaking you should not modify these properties directly. Rather you should use the
|
137
|
+
`delete!` method to remove the resource and the `activate!` and `deactivate!` methods respectively.
|
138
|
+
That being said, if you have a Hash/YAML dataset that explicity has `:_exist => false` when that
|
139
|
+
resource is applied to the device, the resource is deleted. This is handy to ensure a specific
|
140
|
+
resource does not exist, for example.
|
141
|
+
|
142
|
+
# PROVIDERS
|
143
|
+
|
144
|
+
Providers enable access to, and information about resources. So how to you bind a provider to a
|
145
|
+
Netconf::SSH (netconf) object? This is done by the provider's `Provider` method. There are
|
146
|
+
two techniques for *bindng* a provider to a netconf object.
|
147
|
+
|
148
|
+
One method is to bind the provider after the call to `Netconf::SSH#open`. For example, if you want
|
149
|
+
to use the L2port provider, you bind it to the netconf object like so:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
|
153
|
+
# creating a netconf object, here login is a hash defining login info
|
154
|
+
|
155
|
+
ndev = Netconf::SSH.new( login )
|
156
|
+
|
157
|
+
# connect to the target
|
158
|
+
|
159
|
+
ndev.open
|
160
|
+
|
161
|
+
# bind providers to this object
|
162
|
+
|
163
|
+
Junos::Ez::Provider( ndev )
|
164
|
+
Junos::Ez::L2ports::Provider( ndev, :l2_ports )
|
165
|
+
```
|
166
|
+
|
167
|
+
But let's say that you want to create multiple `Netconf::SSH` objects and you don't want to
|
168
|
+
programmatically do the binding each time? You can define a new class inheriting `Netconf::SSH` and
|
169
|
+
overload the `open` method, for example:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
class MyJunosSwitch < Netconf::SSH
|
173
|
+
def open
|
174
|
+
# must be first to open the connection to the target
|
175
|
+
super
|
176
|
+
|
177
|
+
# bind init provider, this will retrieve facts
|
178
|
+
Junos::Ez::Provider( self )
|
179
|
+
|
180
|
+
# bind other providers you want this object to have
|
181
|
+
Junos::Ez::L2ports::Provider( self, :l2_ports )
|
182
|
+
Junos::Ez::Vlans::Provider( self, :vlans )
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# now open a few devices ...
|
187
|
+
|
188
|
+
dev1 = MyJunosSwitch.new( login )
|
189
|
+
dev2 = MyJunosSwitch.new( login )
|
190
|
+
|
191
|
+
dev1.open
|
192
|
+
dev2.open
|
193
|
+
|
194
|
+
pp dev1.vlans.list
|
195
|
+
pp dev2.vlans.list
|
196
|
+
|
197
|
+
dev1.close
|
198
|
+
dev2.close
|
199
|
+
```
|
200
|
+
|
201
|
+
There are a few things to note on these example:
|
202
|
+
|
203
|
+
1. This framework is built around the NETCONF gem as all of the underlying code access the Junos XML
|
204
|
+
API via the NETCONF protocol
|
205
|
+
|
206
|
+
2. You **MUST** use the `Junos::Ez::Provider` before any other providers as this sets up the `Netconf::SSH`
|
207
|
+
object for future bindings and reads the `facts` from the target. These facts can then be
|
208
|
+
used by other provider libraries to abstract target specific differences
|
209
|
+
|
210
|
+
3. **You** get to chose the provider instance variable name (in this case `l2_ports`, there are is no
|
211
|
+
hard-coding going on in this framework, yo! (except for the `facts` variable)
|
212
|
+
|
213
|
+
## Listing Providers
|
214
|
+
|
215
|
+
When you bind providers to a netconf object, you can always get a list of what exists:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
pp ndev.providers
|
219
|
+
|
220
|
+
-> [:l1_ports, :ip_ports, :l2_ports]
|
221
|
+
```
|
222
|
+
|
223
|
+
## Resource List
|
224
|
+
|
225
|
+
You can obtain a list of managed resources using the `list` or `list!` method. This method
|
226
|
+
will return an Array of names. Again these names could be simple strings or complex values.
|
227
|
+
The `list!` method causes the framework to re-read from the device. The `list` method uses the cached value.
|
228
|
+
If there is no-cached value, the framework will read from the device, so you don't need to explicity
|
229
|
+
use `list!` unless you need to force the cache update.
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
pp ndev.l2_ports.list
|
233
|
+
|
234
|
+
-> ["fe-0/0/2", "fe-0/0/3", "fe-0/0/6"]
|
235
|
+
```
|
236
|
+
|
237
|
+
## Resource Catalog
|
238
|
+
|
239
|
+
You can also obtain the provider's catalog, which is a Hash of resources keyed by name and
|
240
|
+
each value is the Hash of the associated properties. The `catalog` and `catalog!` methods
|
241
|
+
work the same as described in the list section above.
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
pp ndev.l2_ports.catalog
|
245
|
+
|
246
|
+
-> {"fe-0/0/2"=>
|
247
|
+
{:_active=>true,
|
248
|
+
:_exist=>true,
|
249
|
+
:vlan_tagging=>true,
|
250
|
+
:tagged_vlans=>["Red", "Green", "Blue"]},
|
251
|
+
"fe-0/0/3"=>{:_active=>true, :_exist=>true, :vlan_tagging=>false},
|
252
|
+
"fe-0/0/6"=>
|
253
|
+
{:_active=>true,
|
254
|
+
:_exist=>true,
|
255
|
+
:vlan_tagging=>false,
|
256
|
+
:untagged_vlan=>"Blue"}}
|
257
|
+
```
|
258
|
+
|
259
|
+
## Selecting a Resource from a Provider
|
260
|
+
|
261
|
+
You select a resource from a provider using the `[]` operator and identifying the resource by *name*. The
|
262
|
+
name could be a simple string as shown in the previous example, or could be a complex name
|
263
|
+
like an Array. If you take a look a the SRX library (separate repo), you can see that
|
264
|
+
the `Junos::Ez::SRX::Policies::Provider` name is an Array of [ from_zone_name, to_zone_name ].
|
265
|
+
|
266
|
+
Here is an example of selecting an ethernet switching port, "ge-0/0/0":
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
port = ndev.l2_ports['ge-0/0/0']
|
270
|
+
```
|
271
|
+
|
272
|
+
When a resource is selected, the framework will automatically `read!` retrieve the configuration.
|
273
|
+
|
274
|
+
## Creating a new Resource
|
275
|
+
|
276
|
+
There are two ways to create a resource. One is using the `create` or `create!` methods.
|
277
|
+
The `create` method is used to create the new resource but not write it do the device. The
|
278
|
+
`create!` method does both the creation and the write to the device. The `create` method
|
279
|
+
can also be given a block, so you can setup the contents of the new resource. Here's an
|
280
|
+
example that creates some config and then deactivates it.
|
281
|
+
|
282
|
+
```ruby
|
283
|
+
ndev.l2_ports.create('ge-0/0/20') do |port|
|
284
|
+
port[:description] = "I am port 20"
|
285
|
+
port[:untagged_vlan] = "Blue"
|
286
|
+
port.write!
|
287
|
+
port.deactivate!
|
288
|
+
end
|
289
|
+
```
|
290
|
+
The above example will also return the new resource object. So you could use the Ruby
|
291
|
+
block as a default initializer, and then continue to make changes to the resource.
|
292
|
+
|
293
|
+
The second way is to simply select a resource by name that doesn't exist. So let's
|
294
|
+
say you want to create a new L2port for `ge-0/0/20`. It would look something like this:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
port = ndev.l2_ports['ge-0/0/20']
|
298
|
+
|
299
|
+
puts "I don't exist" unless port.exists?
|
300
|
+
|
301
|
+
port[:description] = "I am port 20"
|
302
|
+
port[:untagged_vlan] = "Storage"
|
303
|
+
port.write!
|
304
|
+
```
|
data/docs/RE_utils.md
ADDED
data/docs/StaticHosts.md
ADDED
data/docs/Vlans.md
ADDED