junos-ez-stdlib 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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