├── manifests ├── init.pp └── apply_group.pp ├── templates ├── services.set.erb ├── interface.set.erb ├── syslog.text.erb └── event-options.xml.erb ├── examples ├── site.pp ├── services.pp ├── syslogs.pp ├── event-options.pp └── interface.pp ├── files └── junoscripts │ └── event │ └── hello.slax ├── lib ├── facter │ ├── container.rb │ ├── interface_properties.rb │ ├── productmodel.rb │ └── junos.rb └── puppet │ ├── provider │ ├── netdev_device │ │ └── junos.rb │ ├── junos │ │ ├── junos_netdev.rb │ │ ├── junos_netdev_log.rb │ │ ├── junos_netdev_device.rb │ │ ├── junos_interface_classic.rb │ │ ├── junos_vlan.rb │ │ ├── junos_vlan_bd.rb │ │ ├── junos_interface.rb │ │ ├── junos_netdev_res.rb │ │ ├── junos_parent.rb │ │ ├── junos_group.rb │ │ ├── junos_lag.rb │ │ ├── junos_l2_interface.rb │ │ ├── junos_l2_interface_l2ng.rb │ │ └── junos_l2_interface_bd.rb │ ├── netdev_lag │ │ └── junos.rb │ ├── netdev_group │ │ └── junos.rb │ ├── netdev_vlan │ │ └── junos.rb │ ├── netdev_interface │ │ └── junos.rb │ └── netdev_l2_interface │ │ └── junos.rb │ └── type │ └── netdev_group.rb ├── Modulefile ├── metadata.json ├── CHANGELOG.md ├── checksums.json ├── README.md ├── NETDEV-STDLIB.md └── LICENSE /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # == Class: netdev_stdlib_junos 2 | # 3 | class netdev_stdlib_junos {} 4 | -------------------------------------------------------------------------------- /templates/services.set.erb: -------------------------------------------------------------------------------- 1 | <% @services.each do | service | %> 2 | set system services <%= service[0] %> <%= service[1] %> 3 | <% end %> 4 | 5 | -------------------------------------------------------------------------------- /examples/site.pp: -------------------------------------------------------------------------------- 1 | node 'myswitch1234.mycorp.com' { 2 | 3 | netdev_device { $hostname:} 4 | import 'syslogs.pp' 5 | import 'interface.pp' 6 | import 'services.pp' 7 | import 'event-options.pp' 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /templates/interface.set.erb: -------------------------------------------------------------------------------- 1 | <% @interfaces.each do | name, hash | %> 2 | set interfaces <%= name %> description <%= hash['description'] %> unit <%= hash['unit']%> family <%= hash['family'] %> address <%= hash['address']%> 3 | <% end %> 4 | -------------------------------------------------------------------------------- /examples/services.pp: -------------------------------------------------------------------------------- 1 | $services = [ [ 'ftp' ], [ 'ssh' ], [ 'telnet' ], [ 'netconf', 'ssh' ] ] 2 | 3 | netdev_stdlib_junos::apply_group{ 'services_group': 4 | ensure => present, 5 | template_path => 'netdev_stdlib_junos/services.set.erb', 6 | active => true, 7 | } 8 | 9 | -------------------------------------------------------------------------------- /templates/syslog.text.erb: -------------------------------------------------------------------------------- 1 | system { 2 | syslog { 3 | <% @syslog_names.each do | name, details | %> 4 | <% details.each do | detail | %> 5 | file <%= name %> { 6 | <%= detail['facility'] %> <%= detail['level'] %> 7 | } 8 | <% end %> 9 | <% end %> 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /files/junoscripts/event/hello.slax: -------------------------------------------------------------------------------- 1 | version 1.0; 2 | ns junos = "http://xml.juniper.net/junos/*/junos"; 3 | ns xnm = "http://xml.juniper.net/xnm/1.1/xnm"; 4 | ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0"; 5 | import "../import/junos.xsl"; 6 | /* Define arguments */ 7 | 8 | match / { 9 | { 10 | expr jcs:syslog("external.info", "Hello"); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lib/facter/container.rb: -------------------------------------------------------------------------------- 1 | # Fact: container 2 | # 3 | # Purpose: 4 | # Returns the container type. 5 | # 6 | # Test: 7 | # # facter -p container 8 | # docker 9 | # 10 | # Caveats: 11 | # 12 | 13 | Facter.add(:container) do 14 | setcode do 15 | query = "/docker" 16 | arr = File.readlines("/proc/1/cgroup").grep /#{query}/i 17 | if arr.any? 18 | "docker" 19 | else 20 | false 21 | end 22 | end 23 | end -------------------------------------------------------------------------------- /Modulefile: -------------------------------------------------------------------------------- 1 | name 'juniper-netdev_stdlib_junos' 2 | version '2.1.2' 3 | source 'https://github.com/Juniper/puppet-netdev-stdlib-junos' 4 | author 'Raja Shekar Mekala, Juniper Networks' 5 | license 'See LICENSE file' 6 | summary 'Junos Provider code for Networking Device (netdev stdlib) Library' 7 | 8 | description 'Netdev is a vendor-neutral network abstraction framework developed by Juniper Networks and contributed freely to the DevOps community.' 9 | 10 | project_page 'https://github.com/Juniper/puppet-netdev-stdlib-junos' 11 | 12 | dependency 'netdevops/netdev_stdlib', '>= 1.0.0' 13 | -------------------------------------------------------------------------------- /examples/syslogs.pp: -------------------------------------------------------------------------------- 1 | $syslog_names = { 2 | 'messages' => [ { 'facility' => 'any', 3 | 'level' => 'critical' }, 4 | { 'facility' => 'authorization', 5 | 'level' => 'info' } ], 6 | 'interactive-commands' => [ { 'facility' => 'interactive-commands', 7 | 'level' => 'error'} ] 8 | } 9 | 10 | netdev_stdlib_junos::apply_group{ 'syslog_group': 11 | ensure => present, 12 | template_path => 'netdev_stdlib_junos/syslog.text.erb', 13 | active => true, 14 | } 15 | -------------------------------------------------------------------------------- /templates/event-options.xml.erb: -------------------------------------------------------------------------------- 1 | <% @event_script.each do | script | %> 2 | 3 | 4 | 5 | <%=script%> 6 | 7 | 8 | 9 | <% end %> 10 | 11 | <% @policy.each do | key, val | %> 12 | 13 | 14 | <%=key%> 15 | <% val['events'].each do | event | %> 16 | <%=event%> 17 | <% end %> 18 | 19 | 20 | <%=val['event-script']%> 21 | 22 | 23 | 24 | 25 | <% end %> 26 | -------------------------------------------------------------------------------- /examples/event-options.pp: -------------------------------------------------------------------------------- 1 | $policy = { 2 | 'p1' => { 3 | 'events' => [ 'TEST' ], 4 | 'action' => 'then', 5 | 'event-script' => 'hello.slax' 6 | } 7 | } 8 | $event_script = [ 'hello.slax' ] 9 | 10 | file { '/var/db/scripts/event/hello.slax': 11 | mode => '0644', 12 | source => 'puppet:///modules/netdev_stdlib_junos/junoscripts/event/hello.slax', 13 | } 14 | 15 | netdev_stdlib_junos::apply_group{ 'event_options_group': 16 | ensure => present, 17 | template_path => 'netdev_stdlib_junos/event-options.xml.erb', 18 | active => true, 19 | } 20 | 21 | -------------------------------------------------------------------------------- /manifests/apply_group.pp: -------------------------------------------------------------------------------- 1 | # See README.md for usage information 2 | define netdev_stdlib_junos::apply_group( 3 | $template_path, 4 | $ensure = present, 5 | $active = true, 6 | $config_file_owner = undef, 7 | $config_file_mode = '0664' 8 | ) { 9 | 10 | $path = "/var/tmp/${title}" 11 | $extension = split($template_path, '\.') 12 | if $extension[1] != 'erb' { 13 | $format = $extension[1] 14 | } 15 | else { 16 | $format = 'xml' 17 | } 18 | 19 | file{ $path: 20 | ensure => $ensure, 21 | path => $path, 22 | content => template($template_path), 23 | owner => $config_file_owner, 24 | mode => $config_file_mode, 25 | notify => netdev_group[ $title ], 26 | backup => false 27 | } 28 | 29 | netdev_group{ $title: 30 | ensure => $ensure, 31 | path => $path, 32 | format => $format, 33 | active => $active, 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_device/junos.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 2 | 3 | require 'puppet/provider/junos/junos_parent' 4 | 5 | Puppet::Type.type(:netdev_device).provide(:junos, :parent => Puppet::Provider::Junos) do 6 | 7 | @doc = "Junos Device Managed Resource for auto-require" 8 | 9 | ##### ------------------------------------------------------------ 10 | ##### Device provider methods expected by Puppet 11 | ##### ------------------------------------------------------------ 12 | 13 | def exists? 14 | ready = netdev_create 15 | raise "Unable to obtain Junos configuration exclusive lock" unless ready 16 | true 17 | end 18 | 19 | def create 20 | raise "Unreachable: NETDEV create" 21 | end 22 | 23 | def destroy 24 | raise "Unreachable: NETDEV destroy" 25 | end 26 | 27 | def flush 28 | raise "Unreachable: NETDEV flush" 29 | end 30 | 31 | end 32 | -------------------------------------------------------------------------------- /lib/facter/interface_properties.rb: -------------------------------------------------------------------------------- 1 | # Facts: speed_configurable, duplex_configurable 2 | # 3 | # Purpose: 4 | # Checks if speed and duplex properties of interfaces 5 | # are configurable dependening on the productmodel. 6 | # 7 | # Test: 8 | # # facter -p speed_configurable 9 | # # false 10 | # 11 | # # facter -p duplex_configurable 12 | # # false 13 | # 14 | # Caveats: 15 | # 16 | 17 | Facter.add(:speed_configurable) do 18 | setcode do 19 | case Facter.value("productmodel") 20 | when /PTX10003-80C|PTX10003-160C/ 21 | false 22 | when /QFX10003-80C|QFX10003-160C/ 23 | false 24 | else 25 | true 26 | end 27 | end 28 | end 29 | 30 | 31 | Facter.add(:duplex_configurable) do 32 | setcode do 33 | case Facter.value("productmodel") 34 | when /PTX10003-80C|PTX10003-160C/ 35 | false 36 | when /QFX10003-80C|QFX10003-160C/ 37 | false 38 | else 39 | true 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /examples/interface.pp: -------------------------------------------------------------------------------- 1 | $interfaces = { 'ge-1/2/0' => { 2 | 'unit' => 0, 3 | 'description' => 'to-A', 4 | 'family' => 'inet', 5 | 'address' => '10.10.10.1/30' 6 | }, 7 | 'ge-1/1/1' => { 8 | 'unit' => 0, 9 | 'description' => 'to-B', 10 | 'family' => 'inet', 11 | 'address' => '10.10.10.5/30' 12 | }, 13 | 'ge-1/1/0' => { 14 | 'unit' => 0, 15 | 'description' => 'to-C', 16 | 'family' => 'inet', 17 | 'address' => '10.10.10.9/30' 18 | }, 19 | 'ge-1/2/1' => { 20 | 'unit' => 0, 21 | 'description' => 'to-D', 22 | 'family' => 'inet', 23 | 'address' => '10.21.7.1/30' 24 | } 25 | } 26 | 27 | netdev_stdlib_junos::apply_group{ 'interface_group': 28 | ensure => present, 29 | template_path => 'netdev_stdlib_junos/interface.set.erb', 30 | active => true, 31 | } 32 | -------------------------------------------------------------------------------- /lib/facter/productmodel.rb: -------------------------------------------------------------------------------- 1 | # Fact: productmodel 2 | # 3 | # Purpose: 4 | # Returns the product model of the system. 5 | # 6 | # Test: 7 | # # facter -p productmodel 8 | # JNP10003-160C [PTX10003-160C] 9 | # 10 | # Caveats: 11 | # 12 | 13 | is_docker = (Facter.value(:container) == "docker") 14 | 15 | Facter.add(:productmodel) do 16 | setcode do 17 | 18 | # In case of docker container, open a NETCONF/SSH session 19 | if is_docker 20 | require 'net/netconf/jnpr' 21 | # NETCONF_USER refers to the login username configured for puppet operations 22 | login = { :target => 'localhost', :username => ENV['NETCONF_USER'], 23 | :port => ENV['NETCONF_PORT'] ? ENV['NETCONF_PORT'].to_i : 22 } 24 | @netconf = Netconf::SSH.new(login) 25 | # Else, open an IOProc session 26 | else 27 | require 'net/netconf/jnpr/ioproc' 28 | @netconf = Netconf::IOProc.new 29 | end 30 | @netconf.open 31 | inv_info = @netconf.rpc.get_chassis_inventory 32 | errs = inv_info.xpath('//output')[0] 33 | 34 | if errs && errs.text.include?('This command can only be used on the 35 | master routing engine') 36 | raise Junos::Ez::NoProviderError, 'Puppet can only be used on 37 | master routing engine !!' 38 | end 39 | 40 | chassis = inv_info.xpath('chassis') 41 | @netconf.close 42 | # Return chassis description which contains productmodel. 43 | chassis.xpath('description').text 44 | end 45 | end -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "juniper-netdev_stdlib_junos", 3 | "version": "2.1.0", 4 | "author": "Raja Shekar Mekala, Juniper Networks", 5 | "summary": "Junos Provider code for Networking Device (netdev stdlib) Library", 6 | "license": "See LICENSE file", 7 | "source": "https://github.com/Juniper/puppet-netdev-stdlib-junos", 8 | "project_page": "https://github.com/Juniper/puppet-netdev-stdlib-junos", 9 | "issues_url": "https://github.com/Juniper/puppet-netdev-stdlib-junos/issues", 10 | "description": "Netdev is a vendor-neutral network abstraction framework developed by Juniper Networks and contributed freely to the DevOps community.", 11 | "dependencies": [ 12 | { 13 | "name": "netdevops/netdev_stdlib", 14 | "version_requirement": ">= 1.0.0" 15 | }, 16 | { 17 | "name": "netdevops/netdev_stdlib", 18 | "version_requirement": ">= 1.0.0" 19 | }, 20 | { 21 | "name": "netdevops/netdev_stdlib", 22 | "version_requirement": ">= 1.0.0" 23 | }, 24 | { 25 | "name": "netdevops/netdev_stdlib", 26 | "version_requirement": ">= 1.0.0" 27 | }, 28 | { 29 | "name": "netdevops/netdev_stdlib", 30 | "version_requirement": ">= 1.0.0" 31 | }, 32 | { 33 | "name": "netdevops/netdev_stdlib", 34 | "version_requirement": ">= 1.0.0" 35 | }, 36 | { 37 | "name": "netdevops/netdev_stdlib", 38 | "version_requirement": ">= 1.0.0" 39 | }, 40 | {"name":"netdevops/netdev_stdlib","version_requirement":">= 1.0.0"} 41 | ], 42 | "tags": [ 43 | "juniper", 44 | "junos", 45 | "netdevops", 46 | "network", 47 | "networking" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ### 2013-02-15 4 | * Initial release of code, corresponding to Juniper Early Adopter release 0.8B1.1 5 | * Support for the following Juniper Platforms and software releases 6 | * EX4200, EX4550 - Junos 12.2R3.7 7 | * QFX3500 - Junos 12.2X50-D30.4 8 | * Requires Puppet for Junos OS software package to be installed on Junos OS device. 9 | For documentation, refer to: [Puppet for Junos OS](http://www.juniper.net/techpubs/en_US/release-independent/junos-puppet/information-products/pathway-pages/index.html) 10 | 11 | ### 2013-03-03 12 | * Enhancements to support Juniper MX5 ... MX960 products 13 | 14 | ### 2013-03-15 15 | * Bugfixes, and support for broader Junos products 16 | 17 | ### 2013-03-19 18 | * Updated code to support 'feature' controls, requires netdevops/netdev_stdlib 19 | 20 | ### 2013-03-29 21 | * Bugfixes, ready for release 1.0.0 22 | 23 | ### 2014-09-24 24 | * Bugfixes, upgrade version to 1.0.2 25 | 26 | ### 2015-05-05 27 | * Add support for new JUNOS resource type "netdev_group", upgrade to version 2.0.0 28 | 29 | ### 2015-06-30 30 | * Add support for defined type 'apply_group' and minor bug fixes, upgrade to version 2.0.1-beta 31 | 32 | ### 2016-03-17 33 | * Bug fix: 34 | - Issue #17 Puppet: Error message while executing XML format ERB template 35 | - Issue #20 Issue while configuring LAG on 15.2 junos-x image 36 | * Fixed puppet lint issues 37 | * Upgrade to a stable version 2.0.2 38 | 39 | ### 2019-02-27 40 | * Bug fix: 41 | - Issue with netdev_lag creation and deletion - interfaces arriving as array 42 | entries changed to look like normal interfaces to netconf. 43 | * Upgrade to 2.0.5 44 | 45 | ### 2019-12-05 46 | * Features: 47 | - Added support to run puppet-agent from a container 48 | - Added a new fact conatiner: `docker` or `false` to indicate 49 | if puppet is running in an container environment. 50 | 51 | ### 2020-07-03 52 | * Bug fix: 53 | - Case-insensitive comparison for QFX product models. 54 | * Upgrade to 2.1.1 55 | -------------------------------------------------------------------------------- /lib/facter/junos.rb: -------------------------------------------------------------------------------- 1 | 2 | ### ----------------------------------------------------------------------------- 3 | ### junos_personality 4 | ### ----------------------------------------------------------------------------- 5 | 6 | Facter.add(:junos_personality) do 7 | setcode do 8 | case Facter.value("productmodel") 9 | when /^(ex)|(qfx)|(pvi-model)/i 10 | "JUNOS_switch" 11 | when /^srx(\d){4}/ 12 | "JUNOS_SRX_HE" 13 | when /^srx(\d){3}/ 14 | "JUNOS_SRX_branch" 15 | when /^junosv-firefly/ 16 | "JUNOS_SRX_branch" 17 | when /^mx|^vmx/ 18 | "JUNOS_MX" 19 | when /PTX/ 20 | "JUNOS_switch" 21 | end 22 | end 23 | end 24 | 25 | ### ----------------------------------------------------------------------------- 26 | ### junos_ifd_style [ 'classis', 'switch' ] 27 | ### ----------------------------------------------------------------------------- 28 | 29 | Facter.add(:junos_ifd_style) do 30 | confine :junos_personality => :JUNOS_switch 31 | setcode { "switch" } 32 | end 33 | 34 | Facter.add(:junos_ifd_style) do 35 | setcode { "classic" } 36 | end 37 | 38 | ### ----------------------------------------------------------------------------- 39 | ### junos_switch_style [ 'vlan', 'bridge_domain', 'vlan_l2ng', 'none' ] 40 | ### ----------------------------------------------------------------------------- 41 | 42 | Facter.add(:junos_switch_style) do 43 | confine :junos_personality => [:JUNOS_switch, :JUNOS_SRX_branch] 44 | setcode do 45 | case Facter.value("productmodel") 46 | when /^(ex9)|(ex43)|(pvi-model)/ 47 | "vlan_l2ng" 48 | when /^(qfx5)|(qfx3)/ 49 | Facter.value("kernelmajversion")[0..3].to_f >= 13.2 ? "vlan_l2ng" : "vlan" 50 | else 51 | "vlan" 52 | end 53 | end 54 | end 55 | 56 | Facter.add(:junos_switch_style) do 57 | confine :junos_personality => [:JUNOS_MX, :JUNOS_SRX_HE] 58 | setcode { "bridge_domain" } 59 | end 60 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_netdev.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_netdev.rb 5 | * Version : 2012-11-07 6 | * Platform : All Junos 7 | * Description : 8 | * 9 | * This file contains the code responsible for managing the 10 | * "global" Junos device object. This object owns the 11 | * NETCONF connection as well as the building of the 12 | * candidate configuration (XML) 13 | * 14 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 15 | * 16 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 17 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 18 | * 19 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 20 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 21 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 22 | * CAREFULLY. 23 | * 24 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 25 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 26 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 27 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 28 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 29 | * ALLOWED BY APPLICABLE LAW. 30 | * 31 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 32 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 33 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 34 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 35 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 36 | * THE POSSIBILITY OF SUCH DAMAGES. 37 | =end 38 | 39 | 40 | require 'net/netconf/jnpr' 41 | require 'net/netconf/jnpr/ioproc' 42 | 43 | require 'puppet/provider/junos/junos_netdev_device' 44 | require 'puppet/provider/junos/junos_netdev_res' 45 | require 'puppet/provider/junos/junos_netdev_log' 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_lag/junos.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : puppet/provider/netdev_lag/junos.rb 5 | * Version : 2012-12-03 6 | * Platform : EX | QFX 7 | * Description : 8 | * 9 | * The Provider class definition to implement the 10 | * netdev_lag type. There isn't really anything in 11 | * this file; refer to puppet/provider/junos.rb for details. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 39 | require 'puppet/provider/junos/junos_lag' 40 | 41 | Puppet::Type.type(:netdev_lag).provide(:junos, :parent => Puppet::Provider::Junos::LAG) do 42 | @doc = "Junos Link Aggregation Group" 43 | 44 | has_feature :activable 45 | 46 | ### invoke class method to autogen the default property methods for both Puppet 47 | ### and the netdev module. That's it, yo! 48 | 49 | mk_resource_methods 50 | mk_netdev_resource_methods 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_group/junos.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Ganesh Nalawade 4 | * File : puppet/provider/netdev_groups/junos.rb 5 | * Version : 2014-11-10 6 | * Platform : EX | QFX | MX 7 | * Description : 8 | * 9 | * The Provider class definition to implement the 10 | * netdev_lag type. There isn't really anything in 11 | * this file; refer to puppet/provider/junos.rb for details. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 39 | require 'puppet/provider/junos/junos_group' 40 | 41 | Puppet::Type.type(:netdev_group).provide(:junos_group, :parent => Puppet::Provider::Junos::Group) do 42 | @doc = "Junos Configuration Group" 43 | has_feature :activable 44 | 45 | ### invoke class method to autogen the default property methods for both Puppet 46 | ### and the netdev module. That's it, yo! 47 | 48 | mk_resource_methods 49 | mk_netdev_resource_methods 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_netdev_log.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_netdev_log.rb 5 | * Version : 2012-11-09 6 | * Platform : All Junos 7 | * Description : 8 | * 9 | * This file contains the code responsible for reporting 10 | * logs associated with the Junos Netdev module 11 | * 12 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | module NetdevJunos 38 | module Log 39 | class << self 40 | def err( msg, args = {} ) 41 | Puppet::Util::Log.create({:source => :JUNOS, 42 | :level => :err, 43 | :message => msg }.merge( args )) 44 | end 45 | def notice( msg, args = {} ) 46 | Puppet::Util::Log.create({:source => :JUNOS, 47 | :level => :notice, 48 | :message => msg }.merge( args )) 49 | end 50 | def info( msg, args = {} ) 51 | Puppet::Util::Log.create({:source => :JUNOS, 52 | :level => :info, 53 | :message => msg }.merge( args )) 54 | end 55 | def debug( msg, args = {} ) 56 | Puppet::Util::Log.create({:source => :JUNOS, 57 | :level => :debug, 58 | :message => msg }.merge( args )) 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/puppet/type/netdev_group.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : netdev 3 | * Author : Ganesh Nalawade 4 | * File : puppet/type/netdev_group.rb 5 | * Version : 2014-11-10 6 | * Description : 7 | * 8 | * This file contains the Type definition for the JUNOS 9 | * Group Configuration. The network device module 10 | * separates the physical port controls from the service 11 | * function. Service controls are defined in their 12 | * respective type files; e.g. netdev_group.rb 13 | * 14 | =end 15 | 16 | Puppet::Type.newtype(:netdev_group) do 17 | @doc = "Network Device Group Configuration" 18 | 19 | feature :refreshable, "The provider can restart the service.", 20 | :methods => [:restart] 21 | 22 | ensurable 23 | feature :activable, "The ability to activate/deactive configuration" 24 | 25 | ##### ------------------------------------------------------------- 26 | ##### Parameters 27 | ##### ------------------------------------------------------------- 28 | 29 | newparam( :name, :namevar=>true ) do 30 | desc "Group Name" 31 | end 32 | 33 | ##### ------------------------------------------------------------- 34 | ##### Properties 35 | ##### ------------------------------------------------------------- 36 | 37 | newproperty( :active, :required_features => :activable ) do 38 | desc "Config activation" 39 | defaultto(:true) 40 | newvalues(:true, :false) 41 | end 42 | 43 | newproperty( :path, :namevar => true ) do 44 | desc "Path of JUNOS configuration file" 45 | validate do |value| 46 | unless Puppet::Util.absolute_path?(value) 47 | fail Puppet::Error, "File paths must be fully qualified, not '#{value}'" 48 | end 49 | end 50 | 51 | munge do |value| 52 | ::File.expand_path(value) 53 | end 54 | end 55 | 56 | 57 | newproperty( :format ) do 58 | desc "JUNOS configuration format [set|conf|xml]" 59 | defaultto( "xml" ) 60 | newvalues( "text", "set", "xml" ) 61 | end 62 | 63 | ##### ------------------------------------------------------------- 64 | ##### Auto require the netdev_device resource - 65 | ##### There must be one netdev_device resource defined in the 66 | ##### catalog, it doesn't matter what the name of the device is, 67 | ##### just that one exists. 68 | ##### ------------------------------------------------------------- 69 | 70 | autorequire(:netdev_device) do 71 | netdev = catalog.resources.select{ |r| r.type == :netdev_device }[0] 72 | raise "No netdev_device found in catalog" unless netdev 73 | netdev.title # returns the name of the netdev_device resource 74 | end 75 | 76 | def refresh 77 | provider.refresh 78 | end 79 | end 80 | 81 | -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_vlan/junos.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : puppet/provider/netdev_vlan/junos.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * The Provider class definition to implement the 10 | * netdev_vlan type. There isn't really anything in 11 | * this file; refer to puppet/provider/junos.rb for details. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 39 | require 'puppet/provider/junos/junos_vlan' 40 | 41 | Puppet::Type.type(:netdev_vlan).provide(:junos_vlan, :parent => Puppet::Provider::Junos::Vlan) do 42 | @doc = "Junos VLAN" 43 | 44 | has_features :activable, :describable, :no_mac_learning 45 | confine :junos_switch_style => [:vlan, :vlan_l2ng] 46 | 47 | ### invoke class method to autogen the default property methods for both Puppet 48 | ### and the netdev module. That's it, yo! 49 | 50 | mk_resource_methods 51 | mk_netdev_resource_methods 52 | 53 | end 54 | 55 | require 'puppet/provider/junos/junos_vlan_bd' 56 | 57 | Puppet::Type.type(:netdev_vlan).provide(:junos_bd, :parent => Puppet::Provider::Junos::BridgeDomain) do 58 | @doc = "Junos VLAN for Bridge-Domain" 59 | 60 | has_features :activable, :describable, :no_mac_learning 61 | confine :junos_switch_style => :bridge_domain 62 | 63 | mk_resource_methods 64 | mk_netdev_resource_methods 65 | 66 | end 67 | -------------------------------------------------------------------------------- /checksums.json: -------------------------------------------------------------------------------- 1 | { 2 | "CHANGELOG.md": "cb7842c167de1b74018b7172ef9c99bf", 3 | "LICENSE-JUNIPER": "1ac394e2e858b6ce915ac8fe8f4b881b", 4 | "Modulefile": "8f87a944b0e7d989821033014dd85ddb", 5 | "NETDEV-STDLIB.md": "8e153c36638f399ee10aaedfe602b9f0", 6 | "README.md": "d666002e144782961f4d993cd284eb3b", 7 | "examples/event-options.pp": "10f75323686a6a2bfd687c0734be7e78", 8 | "examples/interface.pp": "182aa30f6997e47e31a2878d9aa19305", 9 | "examples/services.pp": "fee2b548608110aaf42949f024a1ab62", 10 | "examples/site.pp": "ce4f1431222e3a35f58b281edb0822f5", 11 | "examples/syslogs.pp": "fc15cca0427678a6dbce6758ab16a2ef", 12 | "files/junoscripts/event/hello.slax": "81c9c9c4d18eb5d20964b9de272d41ea", 13 | "lib/puppet/provider/junos/junos_group.rb": "0d90d4928b1073bc11a6b0f7b835d7ef", 14 | "lib/puppet/provider/junos/junos_interface.rb": "8506cf47a55ffe77b6f8e9582c35e14c", 15 | "lib/puppet/provider/junos/junos_interface_classic.rb": "f03fbafe01e763ea75b58191e8225e77", 16 | "lib/puppet/provider/junos/junos_l2_interface.rb": "5c788c613677546e3058fd27c357de42", 17 | "lib/puppet/provider/junos/junos_l2_interface_bd.rb": "39777f336925a04eb9e1407a82264c31", 18 | "lib/puppet/provider/junos/junos_l2_interface_l2ng.rb": "086d5129611d2fd66b93b9a5f48233fd", 19 | "lib/puppet/provider/junos/junos_lag.rb": "6d7635b27b857c05794464335f367f1c", 20 | "lib/puppet/provider/junos/junos_netdev.rb": "fb9eb7ddf5c280befa992718d4e28a23", 21 | "lib/puppet/provider/junos/junos_netdev_device.rb": "92324ac7889d9365f46faf45d8d2aa18", 22 | "lib/puppet/provider/junos/junos_netdev_log.rb": "955d700a78b8a1af6a5c64d258cd613f", 23 | "lib/puppet/provider/junos/junos_netdev_res.rb": "51ee224c1ca49eca008e1418cbdf8ee4", 24 | "lib/puppet/provider/junos/junos_parent.rb": "372138d481c5f5659367ca5e8ffc1c8c", 25 | "lib/puppet/provider/junos/junos_vlan.rb": "10fb9d3bda7227089f81c51bce8f675a", 26 | "lib/puppet/provider/junos/junos_vlan_bd.rb": "b018caa792f87b5a12b198e4c8500b29", 27 | "lib/puppet/provider/netdev_device/junos.rb": "b3c23054b61d4b23734338c4b3e85029", 28 | "lib/puppet/provider/netdev_group/junos.rb": "356895ff1b36c4d49b52653c0c981b73", 29 | "lib/puppet/provider/netdev_interface/junos.rb": "747718da1c862c223fdcfeda3f9faaf5", 30 | "lib/puppet/provider/netdev_l2_interface/junos.rb": "89384f7a79c2ced11a636a48383322fb", 31 | "lib/puppet/provider/netdev_lag/junos.rb": "1866cf36ae505c66b5f8d04b2a7ced2c", 32 | "lib/puppet/provider/netdev_vlan/junos.rb": "daf2f5d5735f37102b5915aeb81d6a42", 33 | "lib/puppet/type/netdev_group.rb": "4c062ab923ae00d9cacc2ddd3474d717", 34 | "manifests/apply_group.pp": "7c4bcb1577d47c7e9356d5394489e4d1", 35 | "manifests/init.pp": "bb4f5f7c010a8f8533d79fb5da3e35d7", 36 | "metadata.json": "72d1559ea22279752b982d909040bb61", 37 | "templates/event-options.xml.erb": "1008f5f9b54a8a64aa8c7d4e9643518f", 38 | "templates/interface.set.erb": "240caec8952cab4f932f0452ca3320e6", 39 | "templates/services.set.erb": "3b57c4bea41624ef735c8afdcfc57556", 40 | "templates/syslog.text.erb": "d8d05cd3fc306769b4e98cfedd2b9032" 41 | } -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_interface/junos.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : puppet/provider/netdev_l2_interface/junos.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * The Provider class definition to implement the 10 | * netdev_interface type. There isn't really anything in 11 | * this file; refer to puppet/provider/junos.rb for details. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 39 | 40 | require 'puppet/provider/junos/junos_interface' 41 | 42 | Puppet::Type.type(:netdev_interface).provide(:junos_switch, :parent => Puppet::Provider::Junos::Interface) do 43 | @doc = "Junos Physical Interface" 44 | 45 | has_feature :activable 46 | confine :junos_ifd_style => :switch 47 | confine :junos_switch_style => :vlan 48 | ### invoke class method to autogen the default property methods for both Puppet 49 | ### and the netdev module. That's it, yo! 50 | 51 | mk_resource_methods 52 | mk_netdev_resource_methods 53 | 54 | end 55 | 56 | require 'puppet/provider/junos/junos_interface_classic' 57 | 58 | Puppet::Type.type(:netdev_interface).provide(:junos_classic, :parent => Puppet::Provider::Junos::InterfaceClassic) do 59 | @doc = "Junos Physical Interface, Classic Style" 60 | 61 | has_feature :activable 62 | confine :junos_ifd_style => [:classic, :switch] 63 | confine :junos_switch_style => [:vlan_l2ng, :bridge_domain] 64 | ### invoke class method to autogen the default property methods for both Puppet 65 | ### and the netdev module. That's it, yo! 66 | 67 | mk_resource_methods 68 | mk_netdev_resource_methods 69 | 70 | end 71 | -------------------------------------------------------------------------------- /lib/puppet/provider/netdev_l2_interface/junos.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : puppet/provider/netdev_l2_interface/junos.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * The Provider class definition to implement the 10 | * netdev_l2_interface type. There isn't really anything in 11 | * this file; refer to puppet/provider/junos.rb for details. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..","..")) 39 | require 'puppet/provider/junos/junos_l2_interface' 40 | 41 | Puppet::Type.type(:netdev_l2_interface).provide(:junos_vlan, :parent => Puppet::Provider::Junos::L2Interface ) do 42 | @doc = "Junos L2-switch interface" 43 | 44 | has_feature :activable 45 | confine :junos_switch_style => :vlan 46 | 47 | ### invoke class method to autogen the default property methods for both Puppet 48 | ### and the netdev module. That's it, yo! 49 | 50 | mk_resource_methods 51 | mk_netdev_resource_methods 52 | 53 | end 54 | 55 | require 'puppet/provider/junos/junos_l2_interface_bd' 56 | 57 | Puppet::Type.type(:netdev_l2_interface).provide(:junos_bd, :parent => Puppet::Provider::Junos::L2InterfaceBridgeDomain) do 58 | @doc = "Junos L2-switch interface, Bridge-Domain" 59 | 60 | has_features :activable 61 | confine :junos_switch_style => :bridge_domain 62 | 63 | ### invoke class method to autogen the default property methods for both Puppet 64 | ### and the netdev module. That's it, yo! 65 | 66 | mk_resource_methods 67 | mk_netdev_resource_methods 68 | 69 | end 70 | 71 | require 'puppet/provider/junos/junos_l2_interface_l2ng' 72 | 73 | Puppet::Type.type(:netdev_l2_interface).provide(:junos_l2ng, :parent => Puppet::Provider::Junos::L2InterfaceL2NG) do 74 | confine :junos_switch_style => :vlan_l2ng 75 | 76 | @doc = "Junos L2-switch interface, L2NG" 77 | 78 | ### invoke class method to autogen the default property methods for both Puppet 79 | ### and the netdev module. That's it, yo! 80 | 81 | mk_resource_methods 82 | mk_netdev_resource_methods 83 | 84 | end 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OVERVIEW 2 | 3 | Netdev is a vendor-neutral network abstraction framework developed by 4 | Juniper Networks and contributed freely to the DevOps community 5 | 6 | This module contains the Junos specific Provider code implementing 7 | the Resource Types defined in [netdevops/netdev_stdlib](https://github.com/NetdevOps/puppet-netdev-stdlib) 8 | 9 | # EXAMPLE USAGE 10 | 11 | This module has been tested against Puppet agent 2.7.19 and 3.6.1. Here is a short example of a static manifest for a Junos EX switch. This example assumes that you've also installed the Puppet _stdlib_ module as this example uses the _keys_ function. 12 | 13 | ~~~~ 14 | node "myswitch1234.mycorp.com" { 15 | 16 | netdev_device { $hostname: } 17 | 18 | $vlans = { 19 | 'Blue' => { vlan_id => 100, description => "This is a Blue vlan" }, 20 | 'Green' => { vlan_id => 101, description => "This is a Green vLAN" }, 21 | 'Purple' => { vlan_id => 102, description => "This is a Puple vlan" }, 22 | 'Red' => { vlan_id => 103, description => "This is a Red vlan" }, 23 | 'Yellow' => { vlan_id => 104, description => "This is a Yellow vlan" } 24 | } 25 | 26 | create_resources( netdev_vlan, $vlans ) 27 | 28 | $access_ports = [ 29 | 'ge-0/0/0', 30 | 'ge-0/0/1', 31 | 'ge-0/0/2' 32 | ] 33 | 34 | $uplink_ports = [ 35 | 'xe-0/0/0', 36 | 'xe-0/0/2' 37 | ] 38 | 39 | netdev_l2_interface { $access_ports: 40 | untagged_vlan => Blue 41 | } 42 | 43 | netdev_l2_interface { $uplink_ports: 44 | tagged_vlans => keys( $vlans ) 45 | } 46 | 47 | # service variables passed in template file 48 | $services = [ [ 'ftp' ], [ 'ssh' ], [ 'telnet' ], [ 'netconf', 'ssh' ] ] 49 | 50 | netdev_stdlib_junos::apply_group{ "services_group": 51 | template_path => "netdev_stdlib_junos/services.set.erb", 52 | active => true, 53 | ensure => present, 54 | } 55 | 56 | # Syslog variable passed in 'syslog.text.erb' template file 57 | $syslog_names = { 58 | 'messages' => [ { 'facility' => 'any', 'level' => 'critical' }, { 'facility' => 'authorization', 'level' => 'info' } ] , 59 | 'interactive-commands' => [ { 'facility' => 'interactive-commands', 'level' => 'error'} ] 60 | } 61 | 62 | netdev_stdlib_junos::apply_group{ "syslog_group": 63 | template_path => "netdev_stdlib_junos/syslog.text.erb", 64 | active => true, 65 | ensure => present, 66 | } 67 | 68 | # Event-policy variable passed in 'event-options.xml.erb' template file 69 | $policy = { 70 | 'p1' => { 71 | 'events' => [ 'TEST' ], 72 | 'action' => 'then', 73 | 'event-script' => 'hello.slax' 74 | } 75 | } 76 | $event_script = [ 'hello.slax' ] 77 | 78 | # file resource copies the file hello.slax from master to agent 79 | file { '/var/db/scripts/event/hello.slax': 80 | mode => 0644, 81 | source => "puppet:///modules/netdev_stdlib_junos/junoscripts/event/hello.slax", 82 | } 83 | 84 | # Configure event policy and event script 85 | netdev_stdlib_junos::apply_group{ "event_options_group": 86 | template_path => "netdev_stdlib_junos/event-options.xml.erb", 87 | active => true, 88 | ensure => present, 89 | } 90 | } 91 | ~~~~ 92 | 93 | # DEPENDENCIES 94 | 95 | * Puppet >= 2.7.19 96 | * Ruby Gem netconf 0.2.5 97 | * Puppet module netdevops/netdev_stdlib version >= 1.0.0 98 | * Junos OS release and jpuppet image by platform: 99 | * QFX3500, QFX3600: 100 | - JUNOS 12.3X50-D20.1 101 | - jpuppet-qfx-1.0R1.1 102 | * EX4200, EX4500, EX4550: 103 | - JUNOS 12.3R2.5 104 | - jpuppet-ex-1.0R1.1 105 | * MX240, MX480, MX960: 106 | - JUNOS 12.3R2.5 107 | - jpuppet-mx-1.0R1.1 108 | * MX5, MX10, MX40, MX80: 109 | - JUNOS 12.3R2.5 110 | - jpuppet-mx80-1.0R1.1 111 | * QFX5100: 112 | - JUNOS >= 14.2 113 | - jpuppet-3.6.1_1.junos.i386.tgz 114 | * EX4300 115 | - JUNOS >= 14.2 116 | - jpuppet-3.6.1_1.junos.powerpc.tgz 117 | 118 | # INSTALLATION ON PUPPET-MASTER 119 | 120 | * gem install netconf 121 | * puppet module install juniper/netdev_stdlib_junos 122 | 123 | # RESOURCE TYPES 124 | 125 | See RESOURCE-STDLIB.md for documentation and usage examples 126 | 127 | # CONTRIBUTORS 128 | 129 | Juniper Networks is actively contributing to and maintaining this repo. Please contact jnpr-community-netdev@juniper.net for any queries. 130 | 131 | *Contributors:* 132 | 133 | [Ganesh Nalawade](https://github.com/ganeshnalawade), [Priyal Jain](https://github.com/jainpriyal) 134 | 135 | *Former Contributors:* 136 | 137 | [Jeremy Schulman](https://github.com/jeremyschulman) 138 | 139 | # LICENSES 140 | 141 | See [LICENSE](https://github.com/Juniper/puppet-netdev-stdlib-junos/blob/master/LICENSE) 142 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_netdev_device.rb: -------------------------------------------------------------------------------- 1 | ##### --------------------------------------------------------------- 2 | ##### NetdevJunos - there will be only one of these objects 3 | ##### during the puppet agent run. The new object is triggered 4 | ##### by any of the Providers initailzing and creating a 5 | ##### NetdevJunosResource object (next class...) 6 | ##### --------------------------------------------------------------- 7 | 8 | module NetdevJunos 9 | 10 | class Device 11 | 12 | attr_accessor :netconf, :ready, :edits_count 13 | 14 | def initialize( catalog_version ) 15 | 16 | @catalog_version = catalog_version 17 | @edits_count = 0 18 | @ready = false 19 | 20 | fqdn = Facter.value(:fqdn) 21 | Puppet::Transaction.on_transaction_done = self.method(:commit) 22 | is_docker = (Facter.value(:container) == "docker") 23 | 24 | if is_docker 25 | # NETCONF_USER refers to the login username configured for puppet operations 26 | login = { :target => 'localhost', :username => ENV['NETCONF_USER'], 27 | :port => ENV['NETCONF_PORT'] ? ENV['NETCONF_PORT'].to_i : 22 } 28 | @netconf = Netconf::SSH.new(login) 29 | NetdevJunos::Log.debug "Opening a SSH connection from docker container: #{is_docker}" 30 | else 31 | @netconf = Netconf::IOProc.new 32 | @netconf.instance_variable_set :@trans_timeout, nil 33 | NetdevJunos::Log.debug "Opening a local connection: #{fqdn}" 34 | end 35 | 36 | # --- assuming caller is doing exception handling around this! 37 | @netconf.open 38 | 39 | begin 40 | locked = @netconf.rpc.lock_configuration 41 | rescue Netconf::RpcError => e 42 | errmsg = e.to_s 43 | NetdevJunos::Log.err errmsg 44 | else 45 | @ready = true 46 | end 47 | 48 | end 49 | 50 | def edit_config( jcfg_obj, format ) 51 | if jcfg_obj.is_a?(Resource) 52 | edits = Netconf::JunosConfig.new(:TOP) 53 | edits << jcfg_obj 54 | load_config = edits.doc.root 55 | NetdevJunos::Log.debug load_config.to_xml, :tags => [:config, :changes] 56 | 57 | else 58 | load_config = jcfg_obj 59 | end 60 | 61 | # if there is an RPC error (syntax error), it will generate an exception, and 62 | # we want that to "bubble-up" to the calling context so don't 63 | # rescue it here. 64 | 65 | begin 66 | 67 | @edits_count += 1 68 | @netconf.rpc.load_configuration( load_config, :action => 'replace', :format => format ) 69 | 70 | rescue Netconf::RpcError => e 71 | # the load_configuration may yeield rpc-errors that are in fact not errors, 72 | # but merely warnings. Check for that here. 73 | if rpc_errs = e.rsp.xpath('//rpc-error') 74 | # ignore warnings ... 75 | all_count = rpc_errs.count 76 | warn_count = rpc_errs.xpath('error-severity').select{|err| err.text == 'warning'}.count 77 | if all_count - warn_count > 0 78 | @edits_count -= 1 79 | NetdevJunos::Log.err "ERROR: load-configuration\n" + e.rsp.to_xml, :tags => [:config, :fail] 80 | end 81 | end 82 | end # rescue block 83 | 84 | end # edit_config 85 | 86 | ### 87 | ### Commit the candidate configuration, invoked by 88 | ### the 'on transaction complete' hooked in by junos_parent.rb 89 | ### 90 | 91 | def commit 92 | 93 | NetdevJunos::Log.debug "Checking for changes to commit for catalog #{@catalog_version}" 94 | 95 | if @edits_count > 0 96 | 97 | NetdevJunos::Log.info "Committing #{@edits_count} changes.", :tags => [:config, :commit] 98 | 99 | begin 100 | report_show_compare 101 | @netconf.rpc.commit_configuration( :log => "Puppet agent catalog: #{@catalog_version}" ) 102 | rescue Netconf::RpcError => e 103 | NetdevJunos::Log.err "ERROR: Configuration change\n" + e.rsp.to_xml, :tags => [:config, :fail] 104 | else 105 | NetdevJunos::Log.notice "OK: COMMIT success!", :tags => [:config, :success] 106 | ensure 107 | @netconf.rpc.unlock_configuration 108 | end 109 | 110 | end # -- committing changes 111 | 112 | NetdevJunos::Log.debug "Closing NETCONF connection" 113 | begin 114 | @netconf.close 115 | rescue 116 | # ignore - could be in a prior locked condition, and the call to close 117 | # currently raises an RPC error. 118 | end 119 | 120 | end 121 | 122 | def report_show_compare 123 | args = { :database=>'candidate', :compare=>'rollback', :rollback=>'0', :format=>'text' } 124 | compare_rsp = @netconf.rpc.get_configuration( args ) 125 | diff = "\n" + compare_rsp.xpath('//configuration-output').text 126 | NetdevJunos::Log.notice( diff, :tags => [:config, :changes] ) 127 | end 128 | 129 | end #--class 130 | end #-- module 131 | 132 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_interface_classic.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_interface.rb 5 | * Version : 2012-12-04 6 | * Platform : EX | QFX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * Physical interface configuration on platforms that support 11 | * 12 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | require 'puppet/provider/junos/junos_parent' 38 | 39 | class Puppet::Provider::Junos::InterfaceClassic < Puppet::Provider::Junos 40 | 41 | ### --------------------------------------------------------------- 42 | ### triggered from Provider #exists? 43 | ### --------------------------------------------------------------- 44 | 45 | def netdev_res_exists? 46 | 47 | return false unless (ifd = init_resource) 48 | 49 | @ndev_res[:description] = ifd.xpath('description').text.chomp 50 | @ndev_res[:admin] = ifd.xpath('disable').empty? ? :up : :down 51 | @ndev_res[:mtu] = (mtu = ifd.xpath('mtu')[0]).nil? ? -1 : mtu.text.to_i 52 | 53 | @ndev_res[:duplex] = case ifd.xpath('link-mode').text.chomp 54 | when 'full-duplex' then :full 55 | when 'half-duplex' then :half 56 | else 57 | if Facter.value('hardwaremodel') =~ /^ex4300/i 58 | :full 59 | else 60 | :auto 61 | end 62 | end 63 | 64 | @ndev_res[:speed] = ( speed = ifd.xpath('speed')[0] ) ? speed.text : :auto 65 | 66 | return true 67 | end 68 | 69 | ### --------------------------------------------------------------- 70 | ### called from #netdev_exists? 71 | ### --------------------------------------------------------------- 72 | 73 | def init_resource 74 | 75 | resource[:mtu] ||= -1 76 | resource[:description] ||= default_description 77 | 78 | @ndev_res ||= NetdevJunos::Resource.new( self, "interfaces", "interface" ) 79 | 80 | ndev_config = @ndev_res.getconfig 81 | return false unless (ifd = ndev_config.xpath('//interface')[0]) 82 | 83 | @ndev_res.set_active_state( ifd ) 84 | return ifd 85 | end 86 | 87 | def default_description 88 | "Puppet created interface: #{resource[:name]}" 89 | end 90 | 91 | ##### ------------------------------------------------------------- 92 | ##### XML builder methods 93 | ##### ------------------------------------------------------------- 94 | 95 | def xml_change_mtu( xml ) 96 | if resource[:mtu] > 0 97 | xml.mtu resource[:mtu] 98 | else 99 | xml.mtu( Netconf::JunosConfig::DELETE ) 100 | end 101 | end 102 | 103 | def xml_change_admin( xml ) 104 | return xml.disable if resource[:admin] == :down 105 | return if @ndev_res.is_new? 106 | # must be up 107 | xml.disable Netconf::JunosConfig::DELETE 108 | end 109 | 110 | def xml_change_description( xml ) 111 | xml.description resource[:description] 112 | end 113 | 114 | def xml_change_speed( xml ) 115 | if resource[:speed] == :auto 116 | if not @ndev_res.is_new? 117 | xml.speed Netconf::JunosConfig::DELETE 118 | end 119 | else 120 | xml.speed resource[:speed] 121 | end 122 | end 123 | 124 | def xml_change_duplex( xml ) 125 | if Facter.value('hardwaremodel') =~ /^ex4300/i 126 | return (resource[:duplex] == :full) ? nil : (NetdevJunos::Log.notice "On EX4300 switches, the interfaces operate in full duplex mode only. Duplex attribute value \'#{resource[:duplex]}\' will be ignored") 127 | end 128 | if resource[:duplex] == :auto 129 | unless @ndev_res.is_new? 130 | xml.send( :'link-mode', Netconf::JunosConfig::DELETE ) 131 | end 132 | else 133 | xml.send( :'link-mode', case resource[:duplex] 134 | when :full then 'full-duplex' 135 | when :half then 'half-duplex' 136 | end ) 137 | end 138 | end 139 | 140 | end 141 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_vlan.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_vlan.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * VLAN configuration on platforms that support the [edit vlans] 11 | * hierarchy. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | require 'puppet/provider/junos/junos_parent' 39 | 40 | class Puppet::Provider::Junos::Vlan < Puppet::Provider::Junos 41 | 42 | ### -------------------------------------------------------------------- 43 | ### triggered by provider #exists? 44 | ### -------------------------------------------------------------------- 45 | 46 | def netdev_res_exists? 47 | 48 | return false unless (vlan_config = init_resource) 49 | 50 | @ndev_res[:vlan_id] = vlan_config.xpath('vlan-id').text.chomp 51 | @ndev_res[:description] = vlan_config.xpath('description').text.chomp 52 | @ndev_res[:no_mac_learning] = vlan_config.xpath('no-mac-learning').empty? ? :false : :true 53 | 54 | return true 55 | end 56 | 57 | ### -------------------------------------------------------------------- 58 | ### #netdev_retrieve helpers 59 | ### -------------------------------------------------------------------- 60 | 61 | def init_resource 62 | 63 | resource[:description] ||= default_description 64 | 65 | @ndev_res ||= NetdevJunos::Resource.new( self, "vlans", "vlan" ) 66 | 67 | return nil unless (ndev_config = @ndev_res.getconfig) 68 | 69 | return nil unless vlan_config = ndev_config.xpath('//vlan')[0] 70 | 71 | @ndev_res.set_active_state( vlan_config ) 72 | 73 | return vlan_config 74 | end 75 | 76 | def default_description 77 | "Puppet created VLAN: #{resource[:name]}: #{resource[:vlan_id]}" 78 | end 79 | 80 | ##### ------------------------------------------------------------ 81 | ##### XML builder routines, one for each property 82 | ##### ------------------------------------------------------------ 83 | 84 | def xml_change_vlan_id( xml ) 85 | xml.send :"vlan-id", resource[:vlan_id] 86 | on_change_vlan_id( xml ) 87 | end 88 | 89 | def xml_change_description( xml ) 90 | xml.description resource[:description] 91 | end 92 | 93 | def xml_change_no_mac_learning( xml ) 94 | ml = resource[:no_mac_learning] == :false 95 | return if @ndev_res.is_new? and ml 96 | 97 | xml.send( :'no-mac-learning', ml ? Netconf::JunosConfig::DELETE : nil ) 98 | end 99 | 100 | def on_change_vlan_id( xml ) 101 | return unless Facter.value('junos_switch_style') == 'vlan_l2ng' 102 | 103 | # because the L2NG codes the vlan-id values into the interfaces, 104 | # we now need to update all instances of the use of the vlan. Yo! 105 | # so the trick here is to create a false-name by prepending a 106 | # tilde (~) before the vlan_name. Then tinker with the 107 | # associated netdev_l2_interface properties so that it 108 | # triggers the resource to 'do the right thing'. There is 109 | # a dependency in the netdev_l2_interface code on the use 110 | # of the '~' so be aware if you want to muck with it. Yo! 111 | 112 | vlan_name = resource[:name] 113 | vlan_name_new = '~' + vlan_name 114 | 115 | vlan_old = [[ vlan_name ]] 116 | vlan_new = [[ vlan_name_new ]] 117 | 118 | catalog = resource.catalog 119 | 120 | rpc = @ndev_res.rpc 121 | bd_info = rpc.get_vlan_information( :vlan_name => vlan_name ) 122 | intfs = bd_info.xpath('//l2ng-l2rtb-vlan-member-interface') 123 | 124 | intfs.each do |x_int| 125 | ifd_name = x_int.text[/(.*)\./,1] 126 | if l2_intf = catalog.resource( :netdev_l2_interface, ifd_name ) 127 | if l2_intf[:tagged_vlans].include? [vlan_name] 128 | l2_intf[:tagged_vlans] = l2_intf[:tagged_vlans] - vlan_old + vlan_new 129 | end 130 | l2_intf[:untagged_vlan] = vlan_name_new if l2_intf[:untagged_vlan] == vlan_name 131 | else 132 | NetdevJunos::Log.err "Unmanaged VLAN interface: #{ifd_name}" 133 | end 134 | end 135 | 136 | end 137 | 138 | end 139 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_vlan_bd.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_vlan.rb 5 | * Version : 2013-02-19 6 | * Platform : MX 7 | * Description : 8 | * 9 | * 10 | * Copyright (c) 2013 Juniper Networks. All Rights Reserved. 11 | * 12 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 13 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 14 | * 15 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 16 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 17 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 18 | * CAREFULLY. 19 | * 20 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 21 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 22 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 23 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 24 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 25 | * ALLOWED BY APPLICABLE LAW. 26 | * 27 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 28 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 29 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 30 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 31 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 32 | * THE POSSIBILITY OF SUCH DAMAGES. 33 | =end 34 | 35 | require 'puppet/provider/junos/junos_parent' 36 | 37 | class Puppet::Provider::Junos::BridgeDomain < Puppet::Provider::Junos 38 | 39 | ### -------------------------------------------------------------------- 40 | ### triggered by provider #exists? 41 | ### -------------------------------------------------------------------- 42 | 43 | def netdev_res_exists? 44 | 45 | return false unless (vlan_config = init_resource) 46 | 47 | @ndev_res[:vlan_id] = vlan_config.xpath('vlan-id').text.chomp 48 | @ndev_res[:description] = vlan_config.xpath('description').text.chomp 49 | @ndev_res[:no_mac_learning] = 50 | vlan_config.xpath('bridge-options/no-mac-learning').empty? ? :false : :true 51 | 52 | return true 53 | end 54 | 55 | ### -------------------------------------------------------------------- 56 | ### #netdev_retrieve helpers 57 | ### -------------------------------------------------------------------- 58 | 59 | def init_resource 60 | 61 | resource[:description] ||= default_description 62 | 63 | @ndev_res ||= NetdevJunos::Resource.new( self, "bridge-domains", "domain" ) 64 | 65 | return nil unless (ndev_config = @ndev_res.getconfig) 66 | return nil unless vlan_config = ndev_config.xpath('//domain')[0] 67 | 68 | @ndev_res.set_active_state( vlan_config ) 69 | 70 | return vlan_config 71 | end 72 | 73 | def default_description 74 | "Puppet created VLAN: #{resource[:name]}: #{resource[:vlan_id]}" 75 | end 76 | 77 | def on_new_bridge_domain( xml ) 78 | xml.send(:'domain-type', 'bridge') 79 | end 80 | 81 | ##### ------------------------------------------------------------ 82 | ##### XML builder routines, one for each property 83 | ##### ------------------------------------------------------------ 84 | 85 | def xml_change_vlan_id( xml ) 86 | if @ndev_res.is_new? 87 | on_new_bridge_domain( xml ) 88 | end 89 | xml.send :"vlan-id", resource[:vlan_id] 90 | on_change_vlan_id( xml ) 91 | end 92 | 93 | def xml_change_description( xml ) 94 | xml.description resource[:description] 95 | end 96 | 97 | def xml_change_no_mac_learning( xml ) 98 | no_ml = resource[:no_mac_learning] == :false 99 | return if @ndev_res.is_new? and no_ml 100 | 101 | xml.send(:'bridge-options') { 102 | xml.send(:'no-mac-learning', no_ml ? Netconf::JunosConfig::DELETE : nil ) 103 | } 104 | end 105 | 106 | 107 | ##### ------------------------------------------------------------ 108 | ##### Helper, Utilities .... 109 | ##### ------------------------------------------------------------ 110 | 111 | def on_change_vlan_id( xml ) 112 | 113 | # because the MX codes the vlan-id values into the interfaces, 114 | # we now need to update all instances of the use of the vlan. Yo! 115 | # so the trick here is to create a false-name by prepending a 116 | # tilde (~) before the vlan_name. Then tinker with the 117 | # associated netdev_l2_interface properties so that it 118 | # triggers the resource to 'do the right thing'. There is 119 | # a dependency in the netdev_l2_interface code on the use 120 | # of the '~' so be aware if you want to muck with it. Yo! 121 | 122 | vlan_name = resource[:name] 123 | vlan_name_new = '~' + vlan_name 124 | 125 | vlan_old = [[ vlan_name ]] 126 | vlan_new = [[ vlan_name_new ]] 127 | 128 | catalog = resource.catalog 129 | 130 | rpc = @ndev_res.rpc 131 | begin 132 | bd_info = rpc.get_bridge_instance_information( :bridge_domain_name => vlan_name ) 133 | rescue Netconf::RpcError => e 134 | # if rpc is not supported on device return 135 | errmsg = e.to_s 136 | NetdevJunos::Log.notice errmsg 137 | return 138 | end 139 | intfs = bd_info.xpath('//l2rtb-interface-name') 140 | 141 | intfs.each do |x_int| 142 | ifd_name = x_int.text[/(.*)\./,1] 143 | next unless ifd_name 144 | 145 | if l2_intf = catalog.resource( :netdev_l2_interface, ifd_name ) 146 | if l2_intf[:tagged_vlans].include? [vlan_name] 147 | l2_intf[:tagged_vlans] = l2_intf[:tagged_vlans] - vlan_old + vlan_new 148 | end 149 | l2_intf[:untagged_vlan] = vlan_name_new if l2_intf[:untagged_vlan] == vlan_name 150 | else 151 | NetdevJunos::Log.notice "Unmanaged bridge interface: #{x_int.text} for vlan #{vlan_name}" 152 | end 153 | end 154 | 155 | end 156 | 157 | end 158 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_interface.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_interface.rb 5 | * Version : 2012-12-04 6 | * Platform : EX | QFX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * Physical interface configuration on platforms that support 11 | * 12 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | require 'puppet/provider/junos/junos_parent' 38 | 39 | class Puppet::Provider::Junos::Interface < Puppet::Provider::Junos 40 | 41 | ### --------------------------------------------------------------- 42 | ### triggered from Provider #exists? 43 | ### --------------------------------------------------------------- 44 | 45 | def netdev_res_exists? 46 | 47 | return false unless (ifd = init_resource) 48 | 49 | @ndev_res[:description] = ifd.xpath('description').text.chomp 50 | @ndev_res[:admin] = ifd.xpath('disable').empty? ? :up : :down 51 | @ndev_res[:mtu] = (mtu = ifd.xpath('mtu')[0]).nil? ? -1 : mtu.text.to_i 52 | 53 | if $speed_configurable || $duplex_configurable 54 | phy_options = ifd.xpath('ether-options') 55 | 56 | if phy_options.empty? 57 | @ndev_res[:speed] = :auto 58 | @ndev_res[:duplex] = :auto 59 | else 60 | 61 | @ndev_res[:duplex] = case phy_options.xpath('link-mode').text.chomp 62 | when 'full-duplex' then :full 63 | when 'half-duplex' then :half 64 | else :auto 65 | end 66 | 67 | if speed = phy_options.xpath('speed')[0] 68 | @ndev_res[:speed] = speed_from_junos( speed.first_element_child.name ) 69 | else 70 | @ndev_res[:speed] = :auto 71 | end 72 | end 73 | end #### $speed_configurable || $duplex_configurable 74 | return true 75 | end 76 | 77 | ### --------------------------------------------------------------- 78 | ### called from #netdev_exists? 79 | ### --------------------------------------------------------------- 80 | 81 | def init_resource 82 | 83 | resource[:mtu] ||= -1 84 | resource[:description] ||= default_description 85 | 86 | @ndev_res ||= NetdevJunos::Resource.new( self, "interfaces", "interface" ) 87 | 88 | ndev_config = @ndev_res.getconfig 89 | return false unless (ifd = ndev_config.xpath('//interface')[0]) 90 | 91 | @ndev_res.set_active_state( ifd ) 92 | return ifd 93 | end 94 | 95 | def default_description 96 | "Puppet created interface: #{resource[:name]}" 97 | end 98 | 99 | def speed_to_junos( pval ) 100 | case pval 101 | when :'1g' then :'ethernet-1g' 102 | when :'100m' then :'ethernet-100m' 103 | when :'10m' then :'ethernet-10m' 104 | else :auto 105 | end 106 | end 107 | 108 | def speed_from_junos( jval ) 109 | case jval 110 | when 'ethernet-100m' then :'100m' 111 | when 'ethernet-10m' then :'10m' 112 | when 'ethernet-1g' then :'1g' 113 | else :auto 114 | end 115 | end 116 | 117 | ##### ------------------------------------------------------------- 118 | ##### XML builder methods 119 | ##### ------------------------------------------------------------- 120 | 121 | def xml_change_mtu( xml ) 122 | if resource[:mtu] > 0 123 | xml.mtu resource[:mtu] 124 | else 125 | xml.mtu( Netconf::JunosConfig::DELETE ) 126 | end 127 | end 128 | 129 | def xml_change_admin( xml ) 130 | return xml.disable if resource[:admin] == :down 131 | return if @ndev_res.is_new? 132 | # must be up 133 | xml.disable Netconf::JunosConfig::DELETE 134 | end 135 | 136 | def xml_change_description( xml ) 137 | xml.description resource[:description] 138 | end 139 | 140 | def xml_change_speed( xml ) 141 | if $speed_configurable 142 | xml.send(:'ether-options') { 143 | xml.speed { 144 | if resource[:speed] == :auto 145 | if not @ndev_res.is_new? 146 | jval = speed_to_junos( @ndev_res[:speed] ) 147 | xml.send( jval, Netconf::JunosConfig::DELETE ) 148 | end 149 | else 150 | xml.send( speed_to_junos( resource[:speed] )) 151 | end 152 | } 153 | } 154 | end 155 | end 156 | 157 | def xml_change_duplex( xml ) 158 | if $duplex_configurable 159 | xml.send(:'ether-options') { 160 | if resource[:duplex] == :auto 161 | unless @ndev_res.is_new? 162 | xml.send( :'link-mode', Netconf::JunosConfig::DELETE ) 163 | end 164 | else 165 | xml.send( :'link-mode', case resource[:duplex] 166 | when :full then 'full-duplex' 167 | when :half then 'half-duplex' 168 | end ) 169 | end 170 | } 171 | end 172 | end 173 | 174 | end 175 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_netdev_res.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_netdev_log.rb 5 | * Version : 2012-11-09 6 | * Platform : All Junos 7 | * Description : 8 | * 9 | * This file contains the code responsible for resources 10 | * (vlans, l2-interfaces, etc.) that are used by the 11 | * provider child classes. The resource class models the 12 | * data collection management for any give Junos object. 13 | * Each Provider object will create a NetdevJunos::Resource 14 | * object for processing the Provider properties. 15 | * The act of instantiating this class will trigger the 16 | * creation of the "global" NetdevJunos::Device object 17 | * through the use of the 'netdev_get' method 18 | * 19 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 20 | * 21 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 22 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 23 | * 24 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 25 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 26 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 27 | * CAREFULLY. 28 | * 29 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 30 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 31 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 32 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 33 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 34 | * ALLOWED BY APPLICABLE LAW. 35 | * 36 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 37 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 38 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 39 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 40 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 41 | * THE POSSIBILITY OF SUCH DAMAGES. 42 | =end 43 | 44 | 45 | require 'net/netconf/jnpr' 46 | 47 | module NetdevJunos 48 | 49 | class Resource < Netconf::JunosConfig 50 | 51 | attr_reader :edit_item, :rpc 52 | 53 | def initialize( pp_obj, edit_path, edit_item = nil ) 54 | @edit_path = edit_path 55 | @edit_item = edit_item 56 | super( :edit => edit_path, :build => method(:netdev_on_makeconfig_xml) ) 57 | 58 | @pp_obj = pp_obj 59 | @property_hash = pp_obj.instance_variable_get(:@property_hash) 60 | 61 | @rpc = pp_obj.netdev_get.netconf.rpc 62 | 63 | @ndev_hash = Hash.new 64 | @ndev_hash[:active] = :true # config items are active by default 65 | end 66 | 67 | ##### ------------------------------------------------------------ 68 | ##### Hash/parameter methods 69 | ##### ------------------------------------------------------------ 70 | 71 | def [](key) 72 | @ndev_hash[key] 73 | end 74 | 75 | def []=(key,value) 76 | @ndev_hash[key] = value 77 | end 78 | 79 | def update( name ) 80 | return if defined? @deleted 81 | self << {:name => name } 82 | end 83 | 84 | def del( name ) 85 | self << { :name => name, :junos_delete => true } 86 | @deleted = true 87 | end 88 | 89 | ### this method gets called when the provider 90 | ### adds the resource contents to the NetdevJunos 91 | ### managed object. Refer to NetdevJunos#edit_config 92 | 93 | def netdev_on_makeconfig_xml( xml, netdev_res ) 94 | 95 | if netdev_res[:junos_delete] 96 | @pp_obj.netdev_resxml_delete( xml ) 97 | return 98 | end 99 | 100 | if @property_hash.empty? 101 | 102 | @top_xml ||= @pp_obj.netdev_resxml_top( xml ) 103 | unless @pp_obj.ndev_res[:unmanaged_active] 104 | unless @pp_obj.resource[:active] == @pp_obj.ndev_res[:active] 105 | @pp_obj.netdev_resxml_change_active( @top_xml ) 106 | end 107 | end 108 | dot = @pp_obj.netdev_resxml_edit( @top_xml ) 109 | @pp_obj.netdev_resxml_new( dot ) 110 | 111 | else 112 | 113 | @top_xml ||= @pp_obj.netdev_resxml_top( xml ) 114 | 115 | if @property_hash.delete :active 116 | @pp_obj.netdev_resxml_change_active( xml ) 117 | end 118 | 119 | dot = @pp_obj.netdev_resxml_edit( @top_xml ) 120 | @property_hash.each do |k,v| 121 | @pp_obj.send("xml_change_#{k}", dot ) 122 | end 123 | 124 | end 125 | 126 | end 127 | 128 | ### ------------------------------------------------------------ 129 | ### This following is used to generate the XML needed to 130 | ### get the configuration for the given resource from device 131 | ### ------------------------------------------------------------ 132 | 133 | def netdev_resxml_top 134 | cfg = Netconf::JunosConfig.new(:TOP) 135 | xml = cfg.doc 136 | at_ele = cfg.edit_path( xml, @edit_path ) 137 | Nokogiri::XML::Builder.with( at_ele ) do |dot| 138 | @pp_obj.netdev_resxml_top( dot ) 139 | end 140 | return xml 141 | end 142 | 143 | def getconfig 144 | got_config = @rpc.get_configuration( netdev_resxml_top ) 145 | end 146 | 147 | ### ------------------------------------------------------------ 148 | ### Utility Methods 149 | ### ------------------------------------------------------------ 150 | 151 | # method to extract the 'active/inactive' state 152 | # from the Junos configuration item. 153 | 154 | def set_active_state( ndev_xml ) 155 | @ndev_hash[:active] = ndev_xml['inactive'] ? :false : :true 156 | @pp_obj.ndev_res[:name] = @pp_obj.resource[:name] 157 | ndev_xml 158 | end 159 | 160 | def is_new? 161 | return @ndev_hash[:name].nil? 162 | end 163 | 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_parent.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : puppet/provider/junos.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * This file contains the Parent Provider class for all Junos 10 | * Provider classes. This is the *workhorse* of the code 11 | * that encapsulates the main processing tasks. 12 | * 13 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 14 | * 15 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 16 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 17 | * 18 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 19 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 20 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 21 | * CAREFULLY. 22 | * 23 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 24 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 25 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 26 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 27 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 28 | * ALLOWED BY APPLICABLE LAW. 29 | * 30 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 31 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 32 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 33 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 34 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 35 | * THE POSSIBILITY OF SUCH DAMAGES. 36 | =end 37 | 38 | require 'puppet/provider/junos/junos_netdev' 39 | 40 | class Puppet::Provider::Junos < Puppet::Provider 41 | 42 | attr_accessor :ndev_res 43 | 44 | ##### ------------------------------------------------------------ 45 | ##### Device provider methods expected by Puppet 46 | ##### ------------------------------------------------------------ 47 | 48 | def create 49 | Puppet.debug( "#{self.resource.type}: CREATE #{resource[:name]}" ) 50 | end 51 | 52 | def destroy 53 | Puppet.debug( "#{self.resource.type}:: DESTROY #{resource[:name]}" ) 54 | config_del resource[:name] 55 | end 56 | 57 | def exists? 58 | Puppet.debug( "#{self.resource.type}: checking #{resource[:name]}" ) 59 | return false unless netdev_res_exists? 60 | netdev_res_property :name 61 | end 62 | 63 | def flush 64 | if defined? @ndev_res[:name] 65 | Puppet.debug( "#{self.resource.type}:: Flushing #{resource[:name]}" ) 66 | config_update resource[:name] 67 | else 68 | Puppet.debug( "#{self.resource.type}:: Nothing to flush #{resource[:name]}" ) 69 | end 70 | end 71 | 72 | def refresh 73 | Puppet.debug( "#{self.resource.type}: REFRESH #{resource[:name]}" ) 74 | flush 75 | end 76 | 77 | def netdev_create 78 | @@netdev ||= NetdevJunos::Device.new( resource.catalog.version ) 79 | netdev_get 80 | end 81 | 82 | def netdev_get 83 | return (@@netdev.ready) ? @@netdev : nil 84 | end 85 | 86 | ##### ------------------------------------------------------------ 87 | ##### Provider class methods to automatically build property 88 | ##### reader methods 89 | ##### ------------------------------------------------------------ 90 | 91 | def self.netdev 92 | return (@@netdev.ready) ? @@netdev : nil 93 | end 94 | 95 | def self.mk_netdev_resource_methods 96 | (resource_type.validproperties - [:ensure]).sort.each do |prop| 97 | prop_sym = Facter.value(:puppetversion).to_i >= 3 ? prop.intern : symbolize(prop) 98 | define_method(prop_sym) do 99 | netdev_res_property( prop_sym ) 100 | end 101 | end 102 | end 103 | 104 | def netdev_res_property(key) 105 | @ndev_res[key] 106 | end 107 | 108 | def properties 109 | self.class.resource_type.validproperties.flatten - [:ensure, :active] 110 | end 111 | 112 | ##### ------------------------------------------------------------ 113 | ##### Methods that build up the configuration changes 114 | ##### ------------------------------------------------------------ 115 | 116 | def config_update( name ) 117 | @ndev_res.update name 118 | @@netdev.edit_config( @ndev_res, "xml" ) 119 | end 120 | 121 | def config_del( name ) 122 | @ndev_res.del name 123 | # do not invoke @@netdev.edit_config ... just mark the 124 | # item for deletion and it will get picked up by a later 125 | # call to config_edit in #flush 126 | end 127 | 128 | ##### ------------------------------------------------------------ 129 | ##### Methods that generate the XML associated with changes 130 | ##### ------------------------------------------------------------ 131 | 132 | # put the 'dot' inside the top of the config item 133 | 134 | def netdev_resxml_top( xml ) 135 | xml.send( @ndev_res.edit_item ) { 136 | xml.name resource[:name] 137 | return xml 138 | } 139 | end 140 | 141 | # default edit 'dot' assumes top 142 | 143 | def netdev_resxml_edit( xml ) 144 | return xml 145 | end 146 | 147 | # mark the config item for delete 148 | 149 | def netdev_resxml_delete( xml ) 150 | top = netdev_resxml_top( xml ) 151 | par = top.instance_variable_get(:@parent) 152 | par['delete'] = 'delete' 153 | end 154 | 155 | # mark the config item for 'active' or 'inactive' 156 | # this assumes the caller has already set xml to top 157 | 158 | def netdev_resxml_change_active( xml ) 159 | par = xml.instance_variable_get(:@parent) 160 | admin = resource[:active] == :false ? 'inactive' : 'active' 161 | par[admin] = admin 162 | end 163 | 164 | # this assumes the caller has already set xml 165 | # to the 'edit' of the config item 166 | 167 | def netdev_resxml_new( edit_xml ) 168 | self.properties.each do |p| 169 | self.send("xml_change_#{p}", edit_xml ) 170 | end 171 | end 172 | 173 | end 174 | -------------------------------------------------------------------------------- /NETDEV-STDLIB.md: -------------------------------------------------------------------------------- 1 | # USING NETDEV STDLIB 2 | 3 | All netdev resources types include the following two properties: 4 | 5 | * ensure => [ present* | absent ] 6 | * present: configuration will be present 7 | * absent: configuration will not be present 8 | * active => [ true* | false ] 9 | * true: configuration will be present and active 10 | * false: configuration will be present but not active 11 | 12 | ## netdev_device 13 | 14 | There must be a single _netdev_device_ resource present in the catalog. This resource is used to abstract the 15 | configuration controls of the network device. All other netdev resources will have an implicit auto-require, 16 | which causes the netdev_device resource to be evaluated prior to any other netdev resource-type. You can 17 | choose any name for the netdev_device resource as there is no naming dependency in the code. 18 | 19 | The following example uses the Facter variable __$hostname__: 20 | 21 | ````puppet 22 | node "switch1234.mycorp.com" { 23 | 24 | netdev_device { $hostname: } 25 | 26 | } 27 | 28 | ```` 29 | 30 | ## netdev_interface 31 | 32 | This resource models the properties of the physical interfaces: 33 | 34 | * admin => [ up* | down ] 35 | * description => _string_ 36 | * mtu => _number_ 37 | * speed => [ auto* | 100m | 1g | 10g ] 38 | * duplex => [ auto* | half | full ] 39 | 40 | ````puppet 41 | node "switch1234.mycorp.com" { 42 | 43 | netdev_device { $hostname: } 44 | 45 | netdev_interface { "ge-0/0/0": 46 | admin => down, 47 | mtu => 2000 48 | } 49 | 50 | } 51 | ```` 52 | 53 | ## netdev_vlan 54 | 55 | This resource models the properties of a VLAN. VLANs are assigned to interfaces using the *netdev_l2_interface* resource. 56 | 57 | * vlan_id => _number_ 58 | * description => _string_ 59 | 60 | ````puppet 61 | node "switch1234.mycorp.com" { 62 | 63 | netdev_device { $hostname: } 64 | 65 | netdev_vlan { "Blue": 66 | vlan_id => 100, 67 | description => "This is my Blue vlan." 68 | } 69 | 70 | } 71 | ```` 72 | 73 | ## netdev_l2_interface 74 | 75 | This resource models the assignment of VLANs to layer-2 switch ports: 76 | 77 | * description => _string_ 78 | * untagged_vlan => _vlan-name_ 79 | * tagged_vlans => _arrayof vlan-names_ 80 | * vlan_tagging => [ enable | disable ] 81 | 82 | ````puppet 83 | node "switch1234.mycorp.com" { 84 | 85 | netdev_device { $hostname: } 86 | 87 | # access port, packets without VLAN tag 88 | 89 | netdev_l2_interface { 'ge-0/0/0': 90 | untagged_vlan => Red 91 | } 92 | 93 | # trunk port, multiple VLAN taggs 94 | 95 | netdev_l2_interface { 'xe-0/0/0': 96 | tagged_vlans => [ Red, Green, Blue ] 97 | } 98 | 99 | # trunk port, multiple VLAN tags + 100 | # untagged packets go to 'native VLAN' 101 | 102 | netdev_l2_interface { 'xe-0/0/2': 103 | tagged_vlans => [ Red, Green, Blue ], 104 | untagged_vlan => Yellow 105 | } 106 | 107 | } 108 | ```` 109 | 110 | ## netdev_lag 111 | 112 | This resource models the properties of a Link Aggregation Group (LAG): 113 | 114 | * links => _arrayof interface names_ 115 | * minimum_links => _number_ 116 | * lacp => [ disable* | active | passive ] 117 | 118 | ````puppet 119 | node "switch1234.mycorp.com" { 120 | 121 | netdev_device { $hostname: } 122 | 123 | $ae0_ports = [ 'ge-0/0/0', 'ge-1/0/0', 'ge-0/0/2', 'ge-1/0/2' ] 124 | 125 | # we don't want any VLANs configs on the LAG physical ports, 126 | # so ensure there is no config 127 | 128 | netdev_l2_interface { $ae0_ports: ensure => absent } 129 | 130 | # define the LAG port, and then assign VLANs 131 | 132 | netdev_lag { 'ae0': 133 | links => $ae0_ports, 134 | lacp => active, 135 | minimum_links => 2 136 | } 137 | 138 | netdev_l2_interface { 'ae0': 139 | tagged_vlans => [ Red, Green, Blue ] 140 | } 141 | 142 | } 143 | 144 | ```` 145 | 146 | ## netdev_stdlib_junos::apply_group 147 | 148 | This defined type apply_group used for Generic configuration using 149 | puppet template resource: 150 | 151 | * template_path => _Template file path used to generate JUNOS configuration on device_ 152 | * active => [ true | false ] 153 | * ensure => [ present | absent ] 154 | 155 | ````puppet 156 | node "switch1234.mycorp.com" { 157 | 158 | netdev_device { $hostname: } 159 | 160 | # service variables passed in template file 161 | $services = [ [ 'ftp' ], [ 'ssh' ], [ 'telnet' ], [ 'netconf', 'ssh' ] ] 162 | 163 | netdev_stdlib_junos::apply_group{ "services_group": 164 | template_path => "netdev_stdlib_junos/services.set.erb", 165 | active => true, 166 | ensure => present, 167 | } 168 | 169 | # Interface variable passed in 'interface.set.erb' template file 170 | $interfaces = { 'ge-1/2/0' => {'unit' => 0, 'description' => 'to-A', 'family' => 'inet', 'address' => '10.10.10.1/30' }, 171 | 'ge-1/1/1' => {'unit' => 0, 'description' => 'to-B', 'family' => 'inet', 'address' => '10.10.10.5/30' }, 172 | 'ge-1/1/0' => {'unit' => 0, 'description' => 'to-C', 'family' => 'inet', 'address' => '10.10.10.9/30' }, 173 | 'ge-1/2/1' => {'unit' => 0, 'description' => 'to-D', 'family' => 'inet', 'address' => '10.21.7.1/30' } 174 | } 175 | 176 | netdev_stdlib_junos::apply_group{ "interface_group": 177 | template_path => "netdev_stdlib_junos/interface.set.erb", 178 | active => true, 179 | ensure => present, 180 | } 181 | 182 | # Syslog variable passed in 'syslog.text.erb' template file 183 | $syslog_names = { 184 | 'messages' => [ { 'facility' => 'any', 'level' => 'critical' }, { 'facility' => 'authorization', 'level' => 'info' } ] , 185 | 'interactive-commands' => [ { 'facility' => 'interactive-commands', 'level' => 'error'} ] 186 | } 187 | 188 | netdev_stdlib_junos::apply_group{ "syslog_group": 189 | template_path => "netdev_stdlib_junos/syslog.text.erb", 190 | active => true, 191 | ensure => present, 192 | } 193 | 194 | 195 | # Event-policy variable passed in 'event-options.xml.erb' template file 196 | $policy = { 197 | 'p1' => { 198 | 'events' => [ 'TEST' ], 199 | 'action' => 'then', 200 | 'event-script' => 'hello.slax' 201 | } 202 | } 203 | $event_script = [ 'hello.slax' ] 204 | 205 | # file resource copies the file hello.slax from master to agent 206 | file { '/var/db/scripts/event/hello.slax': 207 | mode => 0644, 208 | source => "puppet:///modules/netdev_stdlib_junos/junoscripts/event/hello.slax", 209 | } 210 | 211 | # Configure event policy and event script 212 | netdev_stdlib_junos::apply_group{ "event_options_group": 213 | template_path => "netdev_stdlib_junos/event-options.xml.erb", 214 | active => true, 215 | ensure => present, 216 | } 217 | 218 | } 219 | 220 | ```` 221 | 222 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_group.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Ganesh Nalawade 4 | * File : junos_groups.rb 5 | * Version : 2014-11-10 6 | * Platform : EX | QFX | MX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * Physical interface configuration on platforms that support 11 | * 12 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | require 'puppet/provider/junos/junos_parent' 38 | 39 | class Puppet::Provider::Junos::Group < Puppet::Provider::Junos 40 | 41 | ### --------------------------------------------------------------- 42 | ### triggered from Provider #exists? 43 | ### --------------------------------------------------------------- 44 | 45 | def netdev_res_exists? 46 | resource[:format] ||= 'xml' 47 | return false unless ( grp = init_resource ) 48 | @ndev_res[:name] = grp.xpath('name').text.chomp 49 | return true 50 | end 51 | 52 | ### --------------------------------------------------------------- 53 | ### called from #netdev_exists? 54 | ### --------------------------------------------------------------- 55 | 56 | def init_resource 57 | @ndev_res ||= NetdevJunos::Resource.new( self, "groups" ) 58 | 59 | resource[:format] ||= 'xml' 60 | ndev_config = @ndev_res.getconfig 61 | 62 | raise ArgumentError unless resource[:path] 63 | 64 | grp = ndev_config.xpath("//groups[name=\'#{resource[:name]}\']")[0] 65 | if grp 66 | @ndev_res.set_active_state( grp ) 67 | end 68 | @flush_exec = false 69 | load 70 | 71 | # Check if apply-group is configured 72 | @@rpc ||= Puppet::Provider::Junos.netdev.netconf.rpc 73 | apply_grp_config = @@rpc.get_configuration{ |a_grp| 74 | a_grp.send('apply-groups') 75 | } 76 | apply_grp = apply_grp_config.xpath("//apply-groups=\'#{resource[:name]}\'") 77 | if grp and apply_grp then return grp else return false end 78 | 79 | end 80 | 81 | 82 | def load 83 | return @config = nil if ( resource[:ensure] == :absent ) 84 | admin = '' 85 | if resource[:format].to_s == 'set' 86 | @config = "\ndelete groups #{resource[:name]}\n" + 87 | "edit groups #{resource[:name]}\n" + 88 | File.read( resource[:path] ) 89 | unless resource[:active] == @ndev_res[:active] 90 | admin = resource[:active] == :false ? 'deactivate' : 'activate' 91 | @config += "\nquit\n" 92 | @config += "\n#{admin} groups #{resource[:name]}" 93 | end 94 | 95 | elsif resource[:format].to_s == 'text' 96 | unless resource[:active] == @ndev_res[:active] 97 | admin = resource[:active] == :false ? 'inactive' : 'active' 98 | end 99 | admin += ": " unless admin.empty? 100 | @config = "groups {\n#{admin} replace: #{resource[:name]} {\n" + 101 | File.read( resource[:path] ) + "\n}\n}" 102 | 103 | elsif resource[:format].to_s == 'xml' 104 | @config = File.read( resource[:path]) 105 | end 106 | end 107 | 108 | 109 | ##### ------------------------------------------------------------- 110 | ##### XML builder methods 111 | ##### ------------------------------------------------------------- 112 | def xml_change_path( xml ) 113 | end 114 | 115 | def xml_change_format( xml ) 116 | end 117 | 118 | ##### ------------------------------------------------------------ 119 | ##### XML Resource Building 120 | ##### ------------------------------------------------------------ 121 | 122 | # override default 'top' method 123 | def netdev_resxml_top( xml ) 124 | xml.name resource[:name] 125 | par = xml.instance_variable_get(:@parent) 126 | par['replace'] = 'replace' unless resource[:ensure] == :absent 127 | return xml 128 | end 129 | 130 | def netdev_resxml_edit( xml ) 131 | if @config 132 | xml << @config.to_s 133 | end 134 | return xml 135 | end 136 | 137 | def get_format 138 | if properties.include? :format and @config 139 | return resource[:format] 140 | end 141 | return :xml 142 | end 143 | 144 | def apply_groups 145 | cfg = Netconf::JunosConfig.new(:TOP) 146 | xml = cfg.doc 147 | Nokogiri::XML::Builder.with( xml.at_xpath( 'configuration' )) do |dot| 148 | if @config and resource[:active] == :true 149 | dot.send :'apply-groups', resource[:name] 150 | else 151 | dot.send :'apply-groups', resource[:name], Netconf::JunosConfig::DELETE 152 | end 153 | end 154 | @@netdev.edit_config( xml, "xml" ) 155 | end 156 | 157 | def config_update( name ) 158 | @ndev_res.update name 159 | @format = get_format 160 | if (@format == :set ) or (@format == :text ) 161 | @@netdev.edit_config( @config, @format.to_s ) 162 | else 163 | @@netdev.edit_config( @ndev_res, "xml" ) 164 | end 165 | 166 | if @@netdev.edits_count 167 | apply_groups 168 | end 169 | 170 | end 171 | 172 | ##### ------------------------------------------------------------ 173 | ##### Device provider methods expected by Puppet 174 | ##### ------------------------------------------------------------ 175 | 176 | def flush 177 | unless @flush_exec 178 | ## handle netdev_group attribute change 179 | if netdev_res_exists? 180 | if resource[:ensure] == :absent or 181 | resource[:active] != @ndev_res[:active] 182 | Puppet::Provider::Junos.instance_method(:flush).bind(self).call 183 | @flush_exec = true 184 | else 185 | Puppet.debug( "#{self.resource.type}:: Nothing to flush #{resource[:name]}" ) 186 | end 187 | elsif resource[:ensure] == :present 188 | Puppet::Provider::Junos.instance_method(:flush).bind(self).call 189 | @flush_exec = true 190 | else 191 | Puppet.debug( "#{self.resource.type}:: Nothing to flush #{resource[:name]}" ) 192 | end 193 | end 194 | end 195 | 196 | def refresh 197 | unless @flush_exec 198 | ## handle refresh event from file resource types 199 | Puppet.debug( "#{self.resource.type}: REFRESH #{resource[:name]}" ) 200 | Puppet::Provider::Junos.instance_method(:flush).bind(self).call 201 | @flush_exec = true 202 | end 203 | end 204 | 205 | end 206 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_lag.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_lag.rb 5 | * Version : 2012-12.03 6 | * Platform : EX | QFX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * Link Aggregation Group (LAG) controls 11 | * 12 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | require 'puppet/provider/junos/junos_parent' 38 | 39 | class Puppet::Provider::Junos::LAG < Puppet::Provider::Junos 40 | 41 | ### --------------------------------------------------------------- 42 | ### triggered from Provider #exists? 43 | ### --------------------------------------------------------------- 44 | 45 | def netdev_res_exists? 46 | 47 | ### need to setup the @ifd_ether_options before we return anything 48 | ### since this will be used in the case of a yet-to-be-created LAG 49 | 50 | if Facter.value(:junos_ifd_style) == 'classic' 51 | # all ports in the LAG must be of the same type, so check the first one 52 | # and use it to determine the options stanza name. yo. 53 | l_0 = resource[:links][0].to_s 54 | @ifd_ether_options = (l_0.start_with? 'fe-') ? 'fastether-options' : 'gigether-options' 55 | else 56 | # switching platforms are standardized on the same stanza name. 57 | @ifd_ether_options = 'ether-options' 58 | end 59 | 60 | return false unless ae_config = init_resource 61 | 62 | nc = netdev_get.netconf.rpc 63 | 64 | # ------------------------------------------- 65 | # PROPERTY: links 66 | # ------------------------------------------- 67 | # retrieve details on the ae interface so we can pull the list of member links. In the general 68 | # case there could be a lot of sub-interfaces (units), so we need to handle that case here. We 69 | # only want the IFD names in the @ndev_res hash 70 | 71 | @ndev_res[:links] = get_cookie_links( ae_config ) || [] 72 | 73 | # ------------------------------------------- 74 | # PROPERTY: minimum_links 75 | # ------------------------------------------- 76 | 77 | if mlinks = ae_config.xpath('aggregated-ether-options/minimum-links')[0] 78 | @ndev_res[:minimum_links] = mlinks.text.chomp.to_i; 79 | else 80 | @ndev_res[:minimum_links] = 0 81 | end 82 | 83 | # ------------------------------------------- 84 | # PROPERTY: lacp 85 | # ------------------------------------------- 86 | 87 | lacp = ae_config.xpath('aggregated-ether-options/lacp')[0] 88 | if lacp 89 | @ndev_res[:lacp] = :active if lacp.xpath('active')[0] 90 | @ndev_res[:lacp] = :passive if lacp.xpath('passive')[0] 91 | else 92 | @ndev_res[:lacp] = :disabled 93 | end 94 | 95 | return true 96 | end 97 | 98 | ### --------------------------------------------------------------- 99 | ### called from #netdev_exists? 100 | ### --------------------------------------------------------------- 101 | 102 | def init_resource 103 | 104 | @ndev_res ||= NetdevJunos::Resource.new( self, 'interfaces', 'interface' ) 105 | ndev_config = @ndev_res.getconfig 106 | 107 | return nil unless lagcfg = ndev_config.xpath( '//interface')[0] 108 | 109 | @ndev_res.set_active_state( lagcfg ) 110 | return lagcfg 111 | end 112 | 113 | ### --------------------------------------------------------------- 114 | ### Trim the ifd string to remove double quotes and brackets 115 | ### Converts ["et-0/0/0.0"] to et-0/0/0.0 116 | ### --------------------------------------------------------------- 117 | def trim_ifd(str) 118 | if str[0..1] == '["' && str[-2..-1] == '"]' 119 | # Remove the first 2 and last 2 characters 120 | str[2...-2] 121 | else 122 | str 123 | end 124 | end 125 | 126 | ### --------------------------------------------------------------- 127 | ### Overriding Parent methods 128 | ### --------------------------------------------------------------- 129 | 130 | def destroy 131 | Puppet.debug( "#{self.resource.type}::LAG-DESTROY #{resource[:name]}" ) 132 | 133 | # we need to load the known IFD ports in the lag so we can unbind them 134 | # from the lag. We 'trick' the Puppet agent into processing the 135 | # "change links" method, and from there we can do the unlink and 136 | # removal of the IFD (special-case) 137 | 138 | ae_config = init_resource 139 | @ndev_res[:links] = get_cookie_links( ae_config ) || [] 140 | resource[:links] = [] 141 | @property_hash[:links] = true 142 | 143 | end 144 | 145 | ### on_destroy is called by the 'change links' handler as a special 146 | ### case. that handler will immediately return once on_destroy is done 147 | 148 | def on_destroy( has_links, xml ) 149 | 150 | par = xml.instance_variable_get(:@parent) 151 | dot_ifd = par.at_xpath('ancestor::interfaces') 152 | 153 | has_links.each do |new_ifd| Nokogiri::XML::Builder.with( dot_ifd ) do |dot| 154 | dot.interface { dot.name new_ifd 155 | dot.send(@ifd_ether_options) { 156 | dot.send( :'ieee-802.3ad', Netconf::JunosConfig::DELETE ) 157 | } 158 | } 159 | end 160 | end 161 | 162 | Nokogiri::XML::Builder.with( dot_ifd ) do |dot| 163 | dot.interface( Netconf::JunosConfig::DELETE ) { 164 | dot.name resource[:name] 165 | } 166 | end 167 | 168 | end 169 | 170 | ##### ------------------------------------------------------------ 171 | ##### Utilities 172 | ##### ------------------------------------------------------------ 173 | 174 | def get_cookie_links( cfg ) 175 | cfg.xpath( "apply-macro[name = 'netdev_lag[:links]']/data/name" ).collect { |n| 176 | n.text 177 | } 178 | end 179 | 180 | def set_cookie_links( cfg ) 181 | cfg.send(:'apply-macro', Netconf::JunosConfig::REPLACE ) { 182 | cfg.name 'netdev_lag[:links]' 183 | resource[:links].each{ |ifd| 184 | # Each ifd is an array like this : ["et-1/0/0"] 185 | # Select et-1/0/0 out of this array 186 | cfg.data { cfg.name ifd[0] } 187 | } 188 | } 189 | end 190 | 191 | ##### ------------------------------------------------------------ 192 | ##### XML Resource Building 193 | ##### ------------------------------------------------------------ 194 | 195 | # ------------------------------------------- 196 | # PROPERTY: minimum_links 197 | # ------------------------------------------- 198 | 199 | def xml_change_minimum_links( xml ) 200 | if resource[:minimum_links] > 0 201 | xml.send(:'aggregated-ether-options') { 202 | xml.send( :'minimum-links', resource[:minimum_links] ) 203 | } 204 | else 205 | xml.send(:'aggregated-ether-options') { 206 | xml.send(:'minimum-links', Netconf::JunosConfig::DELETE ) 207 | } 208 | end 209 | end 210 | 211 | # ------------------------------------------- 212 | # PROPERTY: lacp 213 | # ------------------------------------------- 214 | 215 | def xml_change_lacp( xml ) 216 | if resource[:lacp] == :disabled 217 | xml.send(:'aggregated-ether-options') { 218 | xml.lacp( Netconf::JunosConfig::DELETE ) 219 | } 220 | else 221 | xml.send(:'aggregated-ether-options') { 222 | xml.lacp { 223 | xml.send resource[:lacp] 224 | } 225 | } 226 | end 227 | end 228 | 229 | # ------------------------------------------- 230 | # PROPERTY: links 231 | # ------------------------------------------- 232 | 233 | def xml_change_links( xml ) 234 | 235 | has = @ndev_res[:links] || [] 236 | should = resource[:links] || [] 237 | 238 | # -------------------------------------------------------------------- 239 | # this is a special case where we're removing the entire LAG 240 | # we use an empty links list to trigger this action, see #destroy 241 | # method in the above code. 242 | # -------------------------------------------------------------------- 243 | 244 | if should.empty? 245 | on_destroy( has, xml ) 246 | return 247 | end 248 | 249 | set_cookie_links( xml ) 250 | 251 | has = has.map(&:to_s) 252 | should = should.map(&:to_s) 253 | 254 | del = has - should 255 | add = should - has 256 | 257 | par = xml.instance_variable_get(:@parent) 258 | dot_ifd = par.at_xpath('ancestor::interfaces') 259 | 260 | # del entries look like this: 261 | # ["et-0/0/14:0", "et-0/0/16:0", "et-0/0/20:0"] 262 | # Hence, they do not require trimming. 263 | del.each{ |new_ifd| Nokogiri::XML::Builder.with( dot_ifd ) { 264 | |dot| dot.interface { dot.name new_ifd 265 | dot.send(@ifd_ether_options) { 266 | dot.send( :'ieee-802.3ad', Netconf::JunosConfig::DELETE ) 267 | } 268 | }}} 269 | 270 | # add has entries like this: 271 | # ["[\"et-0/0/15:0\"]", "[\"et-0/0/16:0\"]", "[\"et-0/0/20:0\"]"] 272 | # Therefore needs to be trimmed to get ifds. 273 | add.each{ |new_ifd| Nokogiri::XML::Builder.with( dot_ifd ) { 274 | |dot| dot.interface { dot.name trim_ifd(new_ifd) 275 | dot.send(@ifd_ether_options.to_sym) { 276 | dot.send(:'ieee-802.3ad') { 277 | dot.bundle resource[:name] 278 | } 279 | } 280 | }}} 281 | 282 | end 283 | 284 | end 285 | 286 | 287 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright 2015 Juniper Networks, Inc. 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_l2_interface.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_l2_interface.rb 5 | * Version : 2012-11-07 6 | * Platform : EX | QFX | SRX 7 | * Description : 8 | * 9 | * This file contains the Junos specific code to control basic 10 | * Layer 2 interface configuration on platforms that support 11 | * the [edit vlans] hierarchy. L2 interfaces are assumed 12 | * to be at [edit interface unit 0 family ethernet-switching] 13 | * 14 | * Copyright (c) 2012 Juniper Networks. All Rights Reserved. 15 | * 16 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 17 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 18 | * 19 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 20 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 21 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 22 | * CAREFULLY. 23 | * 24 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 25 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 26 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 27 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 28 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 29 | * ALLOWED BY APPLICABLE LAW. 30 | * 31 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 32 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 33 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 34 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 35 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 36 | * THE POSSIBILITY OF SUCH DAMAGES. 37 | =end 38 | 39 | require 'puppet/provider/junos/junos_parent' 40 | 41 | class Puppet::Provider::Junos::L2Interface < Puppet::Provider::Junos 42 | 43 | ### --------------------------------------------------------------- 44 | ### triggered from Provider #exists? 45 | ### --------------------------------------------------------------- 46 | 47 | def netdev_res_exists? 48 | 49 | self.class.init_class_vars 50 | 51 | return false unless (ifl_config = init_resource) 52 | 53 | @ndev_res[:description] = ifl_config.xpath('description').text.chomp 54 | fam_eth_cfg = ifl_config.xpath('family/ethernet-switching') 55 | 56 | return false if fam_eth_cfg.empty? 57 | 58 | netdev_retrieve_fam_eth_info( fam_eth_cfg ) 59 | 60 | return true 61 | end 62 | 63 | ### --------------------------------------------------------------- 64 | ### called from #netdev_exists? 65 | ### --------------------------------------------------------------- 66 | 67 | def init_resource 68 | 69 | @ndev_res ||= NetdevJunos::Resource.new( self, "interfaces" ) 70 | 71 | @ndev_res[:description] = '' 72 | @ndev_res[:vlan_tagging] = :disable 73 | @ndev_res[:untagged_vlan] = '' 74 | @ndev_res[:tagged_vlans] = [] 75 | 76 | resource[:description] ||= default_description 77 | resource[:tagged_vlans] = resource[:tagged_vlans].to_a || [] 78 | resource[:untagged_vlan] ||= '' # if not set in manifest, it is nil 79 | resource[:vlan_tagging] = :enable unless resource[:tagged_vlans].empty? 80 | 81 | ndev_config = @ndev_res.getconfig 82 | 83 | return false unless (ifl_config = ndev_config.xpath('//interface/unit')[0]) 84 | 85 | @ndev_res.set_active_state( ifl_config ) 86 | 87 | return ifl_config 88 | end 89 | 90 | def default_description 91 | "Puppet created netdev_l2_interface: #{resource[:name]}" 92 | end 93 | 94 | def netdev_retrieve_fam_eth_info( fam_eth_cfg ) 95 | 96 | @ndev_res[:vlan_tagging] = fam_eth_cfg.xpath('port-mode').text.chomp == 'trunk' ? :enable : :disable 97 | 98 | # --- access port 99 | 100 | if @ndev_res[:vlan_tagging] == :disable 101 | @ndev_res[:untagged_vlan] = fam_eth_cfg.xpath('vlan/members').text.chomp || '' 102 | return 103 | end 104 | 105 | # --- trunk port 106 | 107 | @ndev_res[:untagged_vlan] = fam_eth_cfg.xpath('native-vlan-id').text.chomp 108 | @ndev_res[:tagged_vlans] = fam_eth_cfg.xpath('vlan/members').collect { |v| v.text.chomp } 109 | end 110 | 111 | def is_trunk? 112 | @ndev_res[:vlan_tagging] == :enable 113 | end 114 | 115 | def should_trunk? 116 | resource[:vlan_tagging] == :enable 117 | end 118 | 119 | def mode_changed? 120 | @ndev_res[:name].nil? or (resource[:vlan_tagging] != @ndev_res[:vlan_tagging]) 121 | end 122 | 123 | ##### ------------------------------------------------------------ 124 | ##### XML Resource Building 125 | ##### ------------------------------------------------------------ 126 | 127 | # override default 'top' method to create the unit sub-interface 128 | 129 | def netdev_resxml_top( xml ) 130 | xml.interface { 131 | xml.name resource[:name] 132 | xml.unit { 133 | xml.name '0' 134 | return xml 135 | } 136 | } 137 | end 138 | 139 | # override default 'edit' method to place 'dot' inside 140 | # the family ethernet-switching stanza 141 | 142 | def netdev_resxml_edit( xml ) 143 | xml.family { 144 | xml.send(:'ethernet-switching') { 145 | return xml 146 | } 147 | } 148 | end 149 | 150 | ### 151 | ### :description 152 | ### 153 | 154 | def xml_change_description( xml ) 155 | par = xml.instance_variable_get(:@parent) 156 | 157 | Nokogiri::XML::Builder.with(par.at_xpath('ancestor::unit')) { 158 | |dot| 159 | dot.description resource[:description] 160 | } 161 | end 162 | 163 | #### 164 | #### :vlan_tagging 165 | #### 166 | 167 | def xml_change_vlan_tagging( xml ) 168 | 169 | port_mode = should_trunk? ? 'trunk' : 'access' 170 | xml.send(:'port-mode', port_mode ) 171 | 172 | # when the vlan_tagging value changes then this method 173 | # will trigger updates to the untagged_vlan and tagged_vlans 174 | # resource values as well. 175 | 176 | upd_untagged_vlan( xml ) 177 | upd_tagged_vlans( xml ) 178 | 179 | end 180 | 181 | ### --------------------------------------------------------------- 182 | ### XML:tagged_vlans 183 | ### --------------------------------------------------------------- 184 | 185 | def xml_change_tagged_vlans( xml ) 186 | return if mode_changed? 187 | upd_tagged_vlans( xml ) 188 | end 189 | 190 | def upd_tagged_vlans( xml ) 191 | 192 | return unless should_trunk? 193 | 194 | should = resource[:tagged_vlans] || [] 195 | 196 | if should.empty? 197 | xml.vlan Netconf::JunosConfig::DELETE 198 | return 199 | end 200 | 201 | has = @ndev_res[:tagged_vlans] || [] 202 | has = has.map(&:to_s) 203 | should = should.map(&:to_s) 204 | 205 | del = has - should 206 | add = should - has 207 | 208 | if add or del 209 | Puppet.debug "#{resource[:name]}: Adding VLANS: [#{add.join(',')}]" unless add.empty? 210 | Puppet.debug "#{resource[:name]}: Deleting VLANS: [#{del.join(',')}]" unless del.empty? 211 | xml.vlan { 212 | del.each { |v| xml.members v, Netconf::JunosConfig::DELETE } 213 | add.each { |v| xml.members v } 214 | } 215 | end 216 | end 217 | 218 | ### --------------------------------------------------------------- 219 | ### XML:untagged_vlan 220 | ### --------------------------------------------------------------- 221 | 222 | def xml_change_untagged_vlan( xml ) 223 | return if mode_changed? 224 | upd_untagged_vlan( xml ) 225 | end 226 | 227 | def upd_untagged_vlan( xml ) 228 | self.class.change_untagged_vlan( self, xml ) 229 | end 230 | 231 | class << self 232 | 233 | # creating some class definitions ... 234 | # this is a bit complicated because we need to handle port-mode 235 | # change transitions; basically dealing with the fact that 236 | # trunk ports use 'native-vlan-id' and access ports have a 237 | # vlan member definition; i.e. they don't use native-vlan-id, ugh. 238 | # Rather than doing all this logic as if/then/else statements, 239 | # I've opted to using a proc jump-table technique. Lessons 240 | # learned from lots of embedded systems programming :-) 241 | 242 | def initcvar_jmptbl_untagged_vlan 243 | 244 | # auto-hash table 245 | hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)})) 246 | 247 | # ------------------------------------------------------------------ 248 | # - jump table for handling various untagged vlan change use-cases 249 | # ------------------------------------------------------------------ 250 | # There are three criteria for selection: 251 | # | is_trunk | will_trunk | no_untg | 252 | # ------------------------------------------------------------------ 253 | # - will not have untagged vlan 254 | hash[false][false][true] = self.method(:ac_ac_nountg) 255 | hash[false][true][true] = self.method(:ac_tr_nountg) 256 | hash[true][false][true] = self.method(:tr_ac_nountg) 257 | hash[true][true][true] = self.method(:tr_tr_nountg) 258 | # - will have untagged vlan 259 | hash[false][false][false] = self.method(:ac_ac_untg) 260 | hash[false][true][false] = self.method(:ac_tr_untg) 261 | hash[true][false][false] = self.method(:tr_ac_untg) 262 | hash[true][true][false] = self.method(:tr_tr_untg) 263 | 264 | hash 265 | end 266 | 267 | ### initialize the jump table once as a class variable 268 | ### this is called from #init_resource 269 | 270 | def init_class_vars 271 | @@untgv_jmptbl ||= initcvar_jmptbl_untagged_vlan 272 | end 273 | 274 | ### invoke the correct method from the jump table 275 | ### based on the three criteria to select the action 276 | 277 | def change_untagged_vlan( this, xml ) 278 | proc = @@untgv_jmptbl[this.is_trunk?][this.should_trunk?][this.resource[:untagged_vlan].empty?] 279 | proc.call( this, xml ) 280 | end 281 | 282 | ### ------------------------------------------------------------- 283 | ### The following are all the change transition functions for 284 | ### each of the use-cases 285 | ### ------------------------------------------------------------- 286 | 287 | def ac_ac_nountg( this, xml ) 288 | xml.vlan Netconf::JunosConfig::DELETE 289 | end 290 | 291 | def ac_tr_nountg( this, xml ) 292 | unless (untg_vlan = this.ndev_res[:tagged_vlans]).empty? 293 | xml.vlan { 294 | xml.members untg_vlan, Netconf::JunosConfig::DELETE 295 | } 296 | end 297 | end 298 | 299 | def tr_ac_nountg( this, xml ) 300 | xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE 301 | xml.vlan( Netconf::JunosConfig::DELETE ) if this.ndev_res[:tagged_vlans] 302 | end 303 | 304 | def tr_tr_nountg( this, xml ) 305 | xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE 306 | end 307 | 308 | def ac_ac_untg( this, xml ) 309 | xml.vlan( Netconf::JunosConfig::REPLACE ) { 310 | xml.members this.resource[:untagged_vlan] 311 | } 312 | end 313 | 314 | def ac_tr_untg( this, xml ) 315 | was_untg_vlan = this.ndev_res[:untagged_vlan] 316 | 317 | xml.vlan( Netconf::JunosConfig::REPLACE ) { 318 | xml.members was_untg_vlan, Netconf::JunosConfig::DELETE if was_untg_vlan 319 | } 320 | xml.send :'native-vlan-id', this.resource[:untagged_vlan] 321 | end 322 | 323 | def tr_ac_untg( this, xml ) 324 | xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE 325 | xml.vlan( Netconf::JunosConfig::REPLACE ) { 326 | xml.members this.resource[:untagged_vlan] 327 | } 328 | end 329 | 330 | def tr_tr_untg( this, xml ) 331 | xml.send :'native-vlan-id', this.resource[:untagged_vlan] 332 | end 333 | 334 | end # class methods for changing untagged_vlan 335 | end 336 | 337 | 338 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_l2_interface_l2ng.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_l2_interface_l2ng.rb 5 | * Version : 2013-03-12 6 | * Platform : EX-L2NG 7 | * Description : 8 | * 9 | * This file implements the netdev_l2_interface type for 10 | * the EX/MX products supporting the L2NG style. 11 | * 12 | * Copyright (c) 2013 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | require 'puppet/provider/junos/junos_parent' 38 | 39 | class Puppet::Provider::Junos::L2InterfaceL2NG < Puppet::Provider::Junos 40 | 41 | ### --------------------------------------------------------------- 42 | ### triggered from Provider #exists? 43 | ### --------------------------------------------------------------- 44 | 45 | def netdev_res_exists? 46 | 47 | resource[:description] ||= default_description 48 | resource[:tagged_vlans] = resource[:tagged_vlans].to_a || [] 49 | resource[:untagged_vlan] ||= '' # if not set in manifest, it is nil 50 | resource[:vlan_tagging] = :enable unless resource[:tagged_vlans].empty? 51 | 52 | self.class.initcvar_for_untagged_vlan 53 | self.class.initcvar_vlanxrefs( resource ) 54 | 55 | return false unless init_ndev_res 56 | 57 | @ndev_res[:description] = '' 58 | @ndev_res[:vlan_tagging] = :disable 59 | @ndev_res[:untagged_vlan] = '' 60 | @ndev_res[:tagged_vlans] = [] 61 | 62 | @ndev_res[:description] = @ifl_config.xpath('description').text.chomp 63 | fam_eth_cfg = @ifl_config.xpath('family/ethernet-switching') 64 | 65 | return false if fam_eth_cfg.empty? 66 | 67 | netdev_retrieve_fam_eth_info( fam_eth_cfg ) 68 | 69 | return true 70 | end 71 | 72 | ### --------------------------------------------------------------- 73 | ### called from #netdev_exists? 74 | ### --------------------------------------------------------------- 75 | 76 | def init_ndev_res 77 | 78 | @ndev_res ||= NetdevJunos::Resource.new( self, "interfaces", "interface" ) 79 | 80 | ndev_config = @ndev_res.getconfig 81 | @ifd_config = ndev_config.xpath('//interface')[0] 82 | return false unless @ifd_config 83 | 84 | @ifl_config = @ifd_config.xpath('unit')[0] 85 | return false unless @ifl_config 86 | 87 | @ndev_res.set_active_state( @ifl_config ) 88 | return true 89 | end 90 | 91 | def default_description 92 | "Puppet created netdev_l2_interface: #{resource[:name]}" 93 | end 94 | 95 | def netdev_retrieve_fam_eth_info( fam_eth_cfg ) 96 | 97 | @ndev_res[:vlan_tagging] = fam_eth_cfg.xpath('interface-mode').text.chomp == 'trunk' ? :enable : :disable 98 | 99 | # --- access port 100 | 101 | if @ndev_res[:vlan_tagging] == :disable 102 | @ndev_res[:untagged_vlan] = fam_eth_cfg.xpath('vlan/members').text.chomp || '' 103 | return 104 | end 105 | 106 | # --- trunk port 107 | 108 | native_vlan_id = @ifd_config.xpath('native-vlan-id').text.chomp; 109 | @ndev_res[:untagged_vlan] = native_vlan_id.empty? ? '' : self.class.vlan_tags_to_names( native_vlan_id ) 110 | 111 | @ndev_res[:tagged_vlans] = fam_eth_cfg.xpath('vlan/members').collect { |v| v.text.chomp } 112 | end 113 | 114 | def is_trunk? 115 | @ndev_res[:vlan_tagging] == :enable 116 | end 117 | 118 | def should_trunk? 119 | resource[:vlan_tagging] == :enable 120 | end 121 | 122 | def mode_changed? 123 | @ndev_res[:name].nil? or (resource[:vlan_tagging] != @ndev_res[:vlan_tagging]) 124 | end 125 | 126 | ### -------------------------------------------------------------------- 127 | ### Routines that work to translate vlan-names to vlan-tagid 128 | ### -------------------------------------------------------------------- 129 | 130 | class << self 131 | 132 | def initcvar_vlanxrefs( resource ) 133 | 134 | @@rpc ||= Puppet::Provider::Junos.netdev.netconf.rpc 135 | @@catalog ||= resource.catalog 136 | @@catalog_netdev_vlan ||= @@catalog.resources.select{ |r| r.type == :netdev_vlan } 137 | 138 | @@vlan_name_hash ||= {} 139 | @@vlan_tag_hash ||= {} 140 | @@vlan_rewrite ||= {} 141 | 142 | # note: resource[:tagged_vlans] is stored as Array-of-Arrays. ugh ... 143 | # so need to grab the .to_s for just the string name... 144 | 145 | all_vlan_names = resource[:tagged_vlans].clone 146 | all_vlan_names << resource[:untagged_vlan] unless resource[:untagged_vlan].empty? 147 | all_vlan_names.each{ |vlan_name| vlanxrefs_addbyname( vlan_name.to_s ) } 148 | 149 | # load the table of currently used VLANs from the device 150 | vlanxrefs_from_junos( resource ) 151 | 152 | end 153 | 154 | def vlanxrefs_from_junos( resource ) 155 | 156 | vlan_if_info = @@rpc.get_vlan_information(:interface => resource[:name] + ".0") 157 | 158 | vlan_names = vlan_if_info.xpath('//l2ifbd-vlan-name') 159 | vlan_names.each do |name| 160 | vlan_name = name.text 161 | 162 | vlan_info = @@rpc.get_vlan_information( :vlan_name => vlan_name, :brief => true ) 163 | dev_vlan_id = vlan_info.xpath('//l2ng-l2rtb-vlan-tag').text.chomp 164 | 165 | if pp_vlan_id = @@vlan_name_hash[vlan_name] 166 | # then Puppet already knows about this vlan, need to see if the vlan-id 167 | # has changed as a result of a netdev_vlan update 168 | if dev_vlan_id != pp_vlan_id 169 | @@vlan_name_hash['~' + vlan_name] = pp_vlan_id 170 | end 171 | else 172 | # Puppet didn't know abou this vlan, so add it to the managed list ... 173 | @@vlan_name_hash[vlan_name] = dev_vlan_id 174 | end 175 | @@vlan_tag_hash[dev_vlan_id] = vlan_name 176 | end # each: bd_name 177 | end 178 | 179 | def vlanxrefs_addbyname( vlan_name ) 180 | return if @@vlan_name_hash[vlan_name] 181 | 182 | # let's see if Puppet knows about this vlan_name ... 183 | # we snarf the tilde (~) in case this is a triggered vlan_name 184 | # change; and the vlan_names in the catalog don't have a tilde. Yo! 185 | 186 | if vlan_res = @@catalog.resource( :netdev_vlan, vlan_name.sub( '~','')) 187 | vlan_id = vlan_res[:vlan_id] 188 | @@vlan_name_hash[vlan_name] = vlan_id 189 | @@vlan_tag_hash[vlan_id] = vlan_name 190 | return 191 | end 192 | 193 | # Pupept doesn't know, so we need to go to the config 194 | # and retrieve what we need ... 195 | 196 | vlan_cfg = @@rpc.get_configuration{|cfg| 197 | cfg.vlans { 198 | cfg.vlan { cfg.name vlan_name 199 | cfg.send(:'vlan-id') 200 | } 201 | }} 202 | 203 | vlan_id = vlan_cfg.xpath('//vlan-id').text.chomp 204 | if vlan_id.empty? 205 | Kernel.raise Puppet::DevError, "requested VLAN #{vlan_name} does not exist!" 206 | return 207 | end 208 | 209 | @@vlan_name_hash[vlan_name] = vlan_id 210 | @@vlan_tag_hash[vlan_id] = vlan_name 211 | 212 | end 213 | 214 | def vlanxrefs_addbytag( switch_name, tag_id ) # returns the vlan name 215 | 216 | # resource[:vlan_id] is a Fixnum, and tag_id is String. So 217 | # convert to Fixnum now for comparison ... 218 | tag_id_i = tag_id.to_i 219 | 220 | p_ndev_vlan = @@catalog_netdev_vlan.select{ |v| v[:vlan_id].to_i == tag_id_i }[0] 221 | if p_ndev_vlan 222 | vlan_name = p_ndev_vlan[:name] 223 | @@vlan_name_hash[vlan_name] = tag_id 224 | @@vlan_tag_hash[tag_id] = vlan_name 225 | vlan_name 226 | else 227 | Kernel.raise Puppet::DevError, "Unknown VLAN, tag-id: #{tag_id} does not exist!" 228 | end 229 | end 230 | 231 | def vlan_tags_to_names( tagid_a ) 232 | if tagid_a.class == Array 233 | tagid_a.collect{ |tag_id| @@vlan_tag_hash[tag_id] || vlanxrefs_addbytag( 'default-switch', tag_id ) } 234 | else 235 | @@vlan_tag_hash[tagid_a] || vlanxrefs_addbytag( 'default-switch', tagid_a ) 236 | end 237 | end 238 | 239 | def vlan_names_to_tags( names_a ) 240 | if names_a.class == Array 241 | tags_a = names_a.collect{ |name| @@vlan_name_hash[name] } 242 | else 243 | @@vlan_name_hash[names_a] 244 | end 245 | end 246 | end 247 | 248 | ##### ------------------------------------------------------------ 249 | ##### XML Resource Building 250 | ##### ------------------------------------------------------------ 251 | 252 | # override default 'edit' method to place 'dot' inside 253 | # the family bridge stanza 254 | 255 | def netdev_resxml_edit( xml ) 256 | xml.unit { 257 | xml.name '0' 258 | xml.family { 259 | xml.send(:'ethernet-switching') { 260 | return xml 261 | } 262 | } 263 | } 264 | end 265 | 266 | ### 267 | ### :description 268 | ### 269 | 270 | def xml_change_description( xml ) 271 | par = xml.instance_variable_get(:@parent) 272 | Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::unit' )) do |dot| 273 | dot.description resource[:description] 274 | end 275 | end 276 | 277 | #### 278 | #### :vlan_tagging 279 | #### 280 | 281 | def xml_change_vlan_tagging( xml ) 282 | 283 | port_mode = should_trunk? ? 'trunk' : 'access' 284 | xml.send :"interface-mode", port_mode 285 | 286 | # when the vlan_tagging value changes then this method 287 | # will trigger updates to the untagged_vlan and tagged_vlans 288 | # resource values as well. 289 | 290 | upd_untagged_vlan( xml ) 291 | upd_tagged_vlans( xml ) 292 | 293 | end 294 | 295 | ### --------------------------------------------------------------- 296 | ### XML:tagged_vlans 297 | ### --------------------------------------------------------------- 298 | 299 | def xml_change_tagged_vlans( xml ) 300 | return if mode_changed? 301 | upd_tagged_vlans( xml ) 302 | end 303 | 304 | def upd_tagged_vlans( xml ) 305 | 306 | return unless should_trunk? 307 | 308 | should = resource[:tagged_vlans] || [] 309 | has = @ndev_res[:tagged_vlans] || [] 310 | 311 | has = has.map(&:to_s) 312 | should = should.map(&:to_s) 313 | 314 | del = has - should 315 | add = should - has 316 | 317 | if add or del 318 | Puppet.debug "#{resource[:name]}: Adding VLANS: [#{add.join(',')}]" unless add.empty? 319 | Puppet.debug "#{resource[:name]}: Deleting VLANS: [#{del.join(',')}]" unless del.empty? 320 | xml.vlan { 321 | del.each{|v| xml.members v, Netconf::JunosConfig::DELETE } 322 | add.each{|v| xml.members v } 323 | } 324 | end 325 | 326 | end 327 | 328 | ### --------------------------------------------------------------- 329 | ### XML:untagged_vlan 330 | ### --------------------------------------------------------------- 331 | 332 | def xml_change_untagged_vlan( xml ) 333 | return if mode_changed? 334 | upd_untagged_vlan( xml ) 335 | end 336 | 337 | def upd_untagged_vlan( xml ) 338 | self.class.change_untagged_vlan( self, xml ) 339 | end 340 | 341 | class << self 342 | 343 | # creating some class definitions ... 344 | # this is a bit complicated because we need to handle port-mode 345 | # change transitions; basically dealing with the fact that 346 | # trunk ports use 'native-vlan-id' and access ports have a 347 | # vlan member definition; i.e. they don't use native-vlan-id, ugh. 348 | # Rather than doing all this logic as if/then/else statements, 349 | # I've opted to using a proc jump-table technique. Lessons 350 | # learned from lots of embedded systems programming :-) 351 | 352 | def initcvar_jmptbl_untagged_vlan 353 | 354 | # auto-hash table 355 | hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)})) 356 | 357 | # ------------------------------------------------------------------ 358 | # - jump table for handling various untagged vlan change use-cases 359 | # ------------------------------------------------------------------ 360 | # There are three criteria for selection: 361 | # | is_trunk | will_trunk | no_untg | 362 | # ------------------------------------------------------------------ 363 | # - will not have untagged vlan 364 | hash[false][false][true] = self.method(:ac_ac_nountg) 365 | hash[false][true][true] = self.method(:ac_tr_nountg) 366 | hash[true][false][true] = self.method(:tr_ac_nountg) 367 | hash[true][true][true] = self.method(:tr_tr_nountg) 368 | # - will have untagged vlan 369 | hash[false][false][false] = self.method(:ac_ac_untg) 370 | hash[false][true][false] = self.method(:ac_tr_untg) 371 | hash[true][false][false] = self.method(:tr_ac_untg) 372 | hash[true][true][false] = self.method(:tr_tr_untg) 373 | 374 | hash 375 | end 376 | 377 | ### initialize the jump table once as a class variable 378 | ### this is called from #init_resource 379 | 380 | def initcvar_for_untagged_vlan 381 | @@untgv_jmptbl ||= initcvar_jmptbl_untagged_vlan 382 | end 383 | 384 | ### invoke the correct method from the jump table 385 | ### based on the three criteria to select the action 386 | 387 | def change_untagged_vlan( this, xml ) 388 | proc = @@untgv_jmptbl[this.is_trunk?][this.should_trunk?][this.resource[:untagged_vlan].empty?] 389 | proc.call( this, xml ) 390 | end 391 | 392 | ### ------------------------------------------------------------- 393 | ### The following are all the change transition functions for 394 | ### each of the use-cases 395 | ### ------------------------------------------------------------- 396 | 397 | def ac_ac_nountg( this, xml ) 398 | NetdevJunos::Log.debug "ac_ac_nountg" 399 | # @@@ a port *MUST* be assigned to a vlan in access mode on MX. 400 | # @@@ generate an error! 401 | Kernel.raise Puppet::DevError, "untagged_vlan missing, port must be assigned to a VLAN" 402 | end 403 | 404 | def ac_tr_nountg( this, xml ) 405 | NetdevJunos::Log.debug "ac_tr_nountg" 406 | ## no action needed; handled already 407 | end 408 | 409 | def tr_ac_nountg( this, xml ) 410 | NetdevJunos::Log.debug "tr_ac_nountg" 411 | # @@@ a port *MUST* be assigned to a vlan in access mode on MX. 412 | # @@@ generate an error! 413 | Kernel.raise Puppet::DevError, "untagged_vlan missing, port must be assigned to a VLAN" 414 | end 415 | 416 | def tr_tr_nountg( this, xml ) 417 | NetdevJunos::Log.debug "tr_tr_nountg" 418 | set_native_vlan_id( this, xml, :delete ) 419 | end 420 | 421 | def ac_ac_untg( this, xml ) 422 | NetdevJunos::Log.debug "ac_ac_untg" 423 | set_vlan_id( this, xml ) 424 | end 425 | 426 | def ac_tr_untg( this, xml ) 427 | NetdevJunos::Log.debug "ac_tr_untg" 428 | set_vlan_id( this, xml, :delete ) if this.ndev_res[:untagged_vlan] 429 | set_native_vlan_id( this, xml ) 430 | end 431 | 432 | def tr_ac_untg( this, xml ) 433 | NetdevJunos::Log.debug "tr_ac_untg" 434 | set_native_vlan_id( this, xml, :delete ) 435 | set_vlan_id( this, xml ) 436 | end 437 | 438 | def tr_tr_untg( this, xml ) 439 | NetdevJunos::Log.debug "tr_tr_untg" 440 | set_native_vlan_id( this, xml ) 441 | end 442 | 443 | ### ------------------------------------------------------------- 444 | ### helper methods, re-usable for each of the transitions 445 | ### ------------------------------------------------------------- 446 | 447 | def set_vlan_id( this, xml, delete = :no ) 448 | xml.vlan( Netconf::JunosConfig::REPLACE ) { 449 | if delete == :delete 450 | xml.members this.resource[:untagged_vlan], Netconf::JunosConfig::DELETE 451 | else 452 | xml.members this.resource[:untagged_vlan] 453 | end 454 | } 455 | end 456 | 457 | def set_native_vlan_id( this, xml, delete = :no ) 458 | par = xml.instance_variable_get(:@parent) 459 | vlan_id = vlan_names_to_tags( this.resource[:untagged_vlan] ) 460 | Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::interface' )) do |dot| 461 | if delete == :delete 462 | dot.send( :'native-vlan-id', Netconf::JunosConfig::DELETE ) 463 | else 464 | dot.send( :'native-vlan-id', vlan_id ) 465 | end 466 | end 467 | end 468 | 469 | end # class methods for changing untagged_vlan 470 | 471 | end 472 | 473 | 474 | -------------------------------------------------------------------------------- /lib/puppet/provider/junos/junos_l2_interface_bd.rb: -------------------------------------------------------------------------------- 1 | =begin 2 | * Puppet Module : Provder: netdev 3 | * Author : Jeremy Schulman 4 | * File : junos_l2_interface_bd.rb 5 | * Version : 2013-03-03 6 | * Platform : MX 7 | * Description : 8 | * 9 | * This file implements the netdev_l2_interface type for 10 | * the MX. 11 | * 12 | * Copyright (c) 2013 Juniper Networks. All Rights Reserved. 13 | * 14 | * YOU MUST ACCEPT THE TERMS OF THIS DISCLAIMER TO USE THIS SOFTWARE, 15 | * IN ADDITION TO ANY OTHER LICENSES AND TERMS REQUIRED BY JUNIPER NETWORKS. 16 | * 17 | * JUNIPER IS WILLING TO MAKE THE INCLUDED SCRIPTING SOFTWARE AVAILABLE TO YOU 18 | * ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS 19 | * DISCLAIMER. PLEASE READ THE TERMS AND CONDITIONS OF THIS DISCLAIMER 20 | * CAREFULLY. 21 | * 22 | * THE SOFTWARE CONTAINED IN THIS FILE IS PROVIDED "AS IS." JUNIPER MAKES NO 23 | * WARRANTIES OF ANY KIND WHATSOEVER WITH RESPECT TO SOFTWARE. ALL EXPRESS OR 24 | * IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY WARRANTY 25 | * OF NON-INFRINGEMENT OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR A 26 | * PARTICULAR PURPOSE, ARE HEREBY DISCLAIMED AND EXCLUDED TO THE EXTENT 27 | * ALLOWED BY APPLICABLE LAW. 28 | * 29 | * IN NO EVENT WILL JUNIPER BE LIABLE FOR ANY DIRECT OR INDIRECT DAMAGES, 30 | * INCLUDING BUT NOT LIMITED TO LOST REVENUE, PROFIT OR DATA, OR 31 | * FOR DIRECT, SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES 32 | * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY ARISING OUT OF THE 33 | * USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF JUNIPER HAS BEEN ADVISED OF 34 | * THE POSSIBILITY OF SUCH DAMAGES. 35 | =end 36 | 37 | 38 | require 'puppet/provider/junos/junos_parent' 39 | 40 | class Puppet::Provider::Junos::L2InterfaceBridgeDomain < Puppet::Provider::Junos 41 | 42 | ### --------------------------------------------------------------- 43 | ### triggered from Provider #exists? 44 | ### --------------------------------------------------------------- 45 | 46 | def netdev_res_exists? 47 | 48 | resource[:description] ||= default_description 49 | resource[:tagged_vlans] = resource[:tagged_vlans].to_a || [] 50 | resource[:untagged_vlan] ||= '' # if not set in manifest, it is nil 51 | resource[:vlan_tagging] = :enable unless resource[:tagged_vlans].empty? 52 | resource[:tagged_vlans] += [resource[:untagged_vlan]] unless resource[:untagged_vlan].empty? 53 | 54 | self.class.initcvar_for_untagged_vlan 55 | self.class.initcvar_vlanxrefs( resource ) 56 | 57 | return false unless init_ndev_res 58 | 59 | @ndev_res[:description] = '' 60 | @ndev_res[:vlan_tagging] = :disable 61 | @ndev_res[:untagged_vlan] = '' 62 | @ndev_res[:tagged_vlans] = [] 63 | 64 | @ndev_res[:description] = @ifl_config.xpath('description').text.chomp 65 | fam_eth_cfg = @ifl_config.xpath('family/bridge') 66 | 67 | return false if fam_eth_cfg.empty? 68 | 69 | netdev_retrieve_fam_eth_info( fam_eth_cfg ) 70 | 71 | return true 72 | end 73 | 74 | ### --------------------------------------------------------------- 75 | ### called from #netdev_exists? 76 | ### --------------------------------------------------------------- 77 | 78 | def init_ndev_res 79 | 80 | @ndev_res ||= NetdevJunos::Resource.new( self, "interfaces", "interface" ) 81 | 82 | ndev_config = @ndev_res.getconfig 83 | @ifd_config = ndev_config.xpath('//interface')[0] 84 | return false unless @ifd_config 85 | 86 | @ifl_config = @ifd_config.xpath('unit')[0] 87 | return false unless @ifl_config 88 | 89 | @ndev_res.set_active_state( @ifl_config ) 90 | return true 91 | end 92 | 93 | def default_description 94 | "Puppet created netdev_l2_interface: #{resource[:name]}" 95 | end 96 | 97 | def netdev_retrieve_fam_eth_info( fam_eth_cfg ) 98 | 99 | @ndev_res[:vlan_tagging] = fam_eth_cfg.xpath('interface-mode').text.chomp == 'trunk' ? :enable : :disable 100 | 101 | # --- access port 102 | 103 | if @ndev_res[:vlan_tagging] == :disable 104 | vlan_id = fam_eth_cfg.xpath('vlan-id').text.chomp || '' 105 | @ndev_res[:untagged_vlan] = self.class.vlan_tags_to_names( vlan_id ) 106 | return 107 | end 108 | 109 | # --- trunk port 110 | native_vlan_id = @ifd_config.xpath('native-vlan-id').text.chomp; 111 | @ndev_res[:untagged_vlan] = native_vlan_id.empty? ? '' : self.class.vlan_tags_to_names( native_vlan_id ) 112 | 113 | vlan_id_list = fam_eth_cfg.xpath('vlan-id-list').collect { |v| v.text.chomp } 114 | @ndev_res[:tagged_vlans] = self.class.vlan_tags_to_names( vlan_id_list ) 115 | end 116 | 117 | def is_trunk? 118 | @ndev_res[:vlan_tagging] == :enable 119 | end 120 | 121 | def should_trunk? 122 | resource[:vlan_tagging] == :enable 123 | end 124 | 125 | def mode_changed? 126 | @ndev_res[:name].nil? or (resource[:vlan_tagging] != @ndev_res[:vlan_tagging]) 127 | end 128 | 129 | ### -------------------------------------------------------------------- 130 | ### Routines that work to translate vlan-names to vlan-tagid 131 | ### -------------------------------------------------------------------- 132 | 133 | class << self 134 | 135 | def initcvar_vlanxrefs( resource ) 136 | 137 | @@rpc ||= Puppet::Provider::Junos.netdev.netconf.rpc 138 | @@catalog ||= resource.catalog 139 | @@catalog_netdev_vlan ||= @@catalog.resources.select{ |r| r.type == :netdev_vlan } 140 | 141 | @@vlan_name_hash ||= {} 142 | @@vlan_tag_hash ||= {} 143 | @@vlan_rewrite ||= {} 144 | 145 | # note: resource[:tagged_vlans] is stored as Array-of-Arrays. ugh ... 146 | # so need to grab the .to_s for just the string name... 147 | 148 | all_vlan_names = resource[:tagged_vlans].clone 149 | all_vlan_names << resource[:untagged_vlan] unless resource[:untagged_vlan].empty? 150 | all_vlan_names.each{ |vlan_name| vlanxrefs_addbyname( vlan_name.to_s ) } 151 | 152 | # load the table of currently used VLANs from the device 153 | vlanxrefs_from_junos( resource ) 154 | 155 | end 156 | 157 | def vlanxrefs_from_junos( resource ) 158 | 159 | bd_if_info = @@rpc.get_bridge_instance_information(:interface => resource[:name] + ".0") 160 | 161 | bd_names = bd_if_info.xpath('l2ald-bd-ifbd-entry/l2ald-ifbd-entry/l2ifbd-bd-name') 162 | bd_names.each do |name| 163 | vlan_name = name.text 164 | 165 | bd_info = @@rpc.get_bridge_instance_information( :bridge_domain_name => vlan_name, :brief => true ) 166 | dev_vlan_id = bd_info.xpath('//l2rtb-bridge-vlan').text.chomp 167 | 168 | if pp_vlan_id = @@vlan_name_hash[vlan_name] 169 | # then Puppet already knows about this vlan, need to see if the vlan-id 170 | # has changed as a result of a netdev_vlan update 171 | if dev_vlan_id != pp_vlan_id 172 | @@vlan_name_hash['~' + vlan_name] = pp_vlan_id 173 | end 174 | else 175 | # Puppet didn't know abou this vlan, so add it to the managed list ... 176 | @@vlan_name_hash[vlan_name] = dev_vlan_id 177 | end 178 | @@vlan_tag_hash[dev_vlan_id] = vlan_name 179 | end # each: bd_name 180 | end 181 | 182 | def vlanxrefs_addbyname( vlan_name ) 183 | return if @@vlan_name_hash[vlan_name] 184 | 185 | # let's see if Puppet knows about this vlan_name ... 186 | # we snarf the tilde (~) in case this is a triggered vlan_name 187 | # change; and the vlan_names in the catalog don't have a tilde. Yo! 188 | 189 | if vlan_res = @@catalog.resource( :netdev_vlan, vlan_name.sub( '~','')) 190 | vlan_id = vlan_res[:vlan_id] 191 | @@vlan_name_hash[vlan_name] = vlan_id 192 | @@vlan_tag_hash[vlan_id] = vlan_name 193 | return 194 | end 195 | 196 | # Pupept doesn't know, so we need to go to the config 197 | # and retrieve what we need ... 198 | 199 | bd_cfg = @@rpc.get_configuration{|cfg| 200 | cfg.send(:'bridge-domains') { 201 | cfg.domain { cfg.name vlan_name 202 | cfg.send(:'vlan-id') 203 | } 204 | }} 205 | 206 | vlan_id = bd_cfg.xpath('//vlan-id').text.chomp 207 | if vlan_id.empty? 208 | Kernel.raise Puppet::DevError, "requested VLAN #{vlan_name} does not exist!" 209 | return 210 | end 211 | 212 | @@vlan_name_hash[vlan_name] = vlan_id 213 | @@vlan_tag_hash[vlan_id] = vlan_name 214 | 215 | end 216 | 217 | ### ------------------------------------------------------------------ 218 | ### vlanxrefs_addbytag and vlan_tags_to_names is called by the 219 | ### routines when the configuration is loaded from the device. 220 | ### ------------------------------------------------------------------ 221 | 222 | def vlanxrefs_addbytag( switch_name, tag_id ) # returns the vlan name 223 | 224 | # resource[:vlan_id] is a Fixnum, and tag_id is String. So 225 | # convert to Fixnum now for comparison ... 226 | tag_id_i = tag_id.to_i 227 | 228 | p_ndev_vlan = @@catalog_netdev_vlan.select{ |v| v[:vlan_id].to_i == tag_id_i }[0] 229 | 230 | if p_ndev_vlan 231 | vlan_name = p_ndev_vlan[:name] 232 | @@vlan_name_hash[vlan_name] = tag_id 233 | @@vlan_tag_hash[tag_id] = vlan_name 234 | vlan_name 235 | else 236 | Kernel.raise Puppet::DevError, "Unknown VLAN by tag-id: #{tag_id} !" 237 | end 238 | end 239 | 240 | def vlan_tags_to_names( tagid_a ) 241 | if tagid_a.class == Array 242 | tagid_a.collect{ |tag_id| @@vlan_tag_hash[tag_id] || vlanxrefs_addbytag( 'default-switch', tag_id ) } 243 | else 244 | @@vlan_tag_hash[tagid_a] || vlanxrefs_addbytag( 'default-switch', tagid_a ) 245 | end 246 | end 247 | 248 | 249 | ### ------------------------------------------------------------------ 250 | ### vlan_names_to_tags is called when this provider is writing the 251 | ### configuration back to the device via the XML routines below ... 252 | ### ------------------------------------------------------------------ 253 | 254 | def vlan_names_to_tags( names_a ) 255 | if names_a.class == Array 256 | tags_a = names_a.collect{ |name| @@vlan_name_hash[name] } 257 | else 258 | @@vlan_name_hash[names_a] 259 | end 260 | end 261 | end 262 | 263 | ##### ------------------------------------------------------------ 264 | ##### XML Resource Building 265 | ##### ------------------------------------------------------------ 266 | 267 | # override default 'edit' method to place 'dot' inside 268 | # the family bridge stanza 269 | 270 | def netdev_resxml_edit( xml ) 271 | xml.unit { 272 | xml.name '0' 273 | xml.family { 274 | xml.send(:'bridge') { 275 | return xml 276 | } 277 | } 278 | } 279 | end 280 | 281 | ### 282 | ### :description 283 | ### 284 | 285 | def xml_change_description( xml ) 286 | par = xml.instance_variable_get(:@parent) 287 | Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::unit' )) do |dot| 288 | dot.description resource[:description] 289 | end 290 | end 291 | 292 | #### 293 | #### :vlan_tagging 294 | #### 295 | 296 | def xml_change_vlan_tagging( xml ) 297 | 298 | port_mode = should_trunk? ? 'trunk' : 'access' 299 | xml.send :"interface-mode", port_mode 300 | 301 | if is_trunk? and not should_trunk? 302 | # trunk --> access 303 | self.class.set_ifd_trunking( xml, false ) 304 | elsif should_trunk? and not is_trunk? 305 | # access --> trunk 306 | self.class.set_ifd_trunking( xml, true ) 307 | end 308 | 309 | # when the vlan_tagging value changes then this method 310 | # will trigger updates to the untagged_vlan and tagged_vlans 311 | # resource values as well. 312 | 313 | upd_untagged_vlan( xml ) 314 | upd_tagged_vlans( xml ) 315 | 316 | end 317 | 318 | ### --------------------------------------------------------------- 319 | ### XML:tagged_vlans 320 | ### --------------------------------------------------------------- 321 | 322 | def xml_change_tagged_vlans( xml ) 323 | return if mode_changed? 324 | upd_tagged_vlans( xml ) 325 | end 326 | 327 | 328 | def upd_tagged_vlans( xml ) 329 | 330 | return unless should_trunk? 331 | 332 | should = resource[:tagged_vlans] || [] 333 | has = @ndev_res[:tagged_vlans] || [] 334 | 335 | has = has.map(&:to_s) 336 | should = should.map(&:to_s) 337 | 338 | del = self.class.vlan_names_to_tags( has - should ) 339 | add = self.class.vlan_names_to_tags( should - has ) 340 | 341 | if add or del 342 | Puppet.debug "#{resource[:name]}: Adding VLANS: [#{add.join(',')}]" unless add.empty? 343 | Puppet.debug "#{resource[:name]}: Deleting VLANS: [#{del.join(',')}]" unless del.empty? 344 | del.each{|tag_id| xml.send( :'vlan-id-list', tag_id, Netconf::JunosConfig::DELETE )} 345 | add.each{|tag_id| xml.send( :'vlan-id-list', tag_id )} 346 | end 347 | 348 | end 349 | 350 | ### --------------------------------------------------------------- 351 | ### XML:untagged_vlan 352 | ### --------------------------------------------------------------- 353 | 354 | def xml_change_untagged_vlan( xml ) 355 | return if mode_changed? 356 | upd_untagged_vlan( xml ) 357 | end 358 | 359 | def upd_untagged_vlan( xml ) 360 | self.class.change_untagged_vlan( self, xml ) 361 | end 362 | 363 | class << self 364 | 365 | # creating some class definitions ... 366 | # this is a bit complicated because we need to handle port-mode 367 | # change transitions; basically dealing with the fact that 368 | # trunk ports use 'native-vlan-id' and access ports have a 369 | # vlan member definition; i.e. they don't use native-vlan-id, ugh. 370 | # Rather than doing all this logic as if/then/else statements, 371 | # I've opted to using a proc jump-table technique. Lessons 372 | # learned from lots of embedded systems programming :-) 373 | 374 | def initcvar_jmptbl_untagged_vlan 375 | 376 | # auto-hash table 377 | hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)})) 378 | 379 | # ------------------------------------------------------------------ 380 | # - jump table for handling various untagged vlan change use-cases 381 | # ------------------------------------------------------------------ 382 | # There are three criteria for selection: 383 | # | is_trunk | will_trunk | no_untg | 384 | # ------------------------------------------------------------------ 385 | # - will not have untagged vlan 386 | hash[false][false][true] = self.method(:ac_ac_nountg) 387 | hash[false][true][true] = self.method(:ac_tr_nountg) 388 | hash[true][false][true] = self.method(:tr_ac_nountg) 389 | hash[true][true][true] = self.method(:tr_tr_nountg) 390 | # - will have untagged vlan 391 | hash[false][false][false] = self.method(:ac_ac_untg) 392 | hash[false][true][false] = self.method(:ac_tr_untg) 393 | hash[true][false][false] = self.method(:tr_ac_untg) 394 | hash[true][true][false] = self.method(:tr_tr_untg) 395 | 396 | hash 397 | end 398 | 399 | ### initialize the jump table once as a class variable 400 | ### this is called from #init_resource 401 | 402 | def initcvar_for_untagged_vlan 403 | @@untgv_jmptbl ||= initcvar_jmptbl_untagged_vlan 404 | end 405 | 406 | ### invoke the correct method from the jump table 407 | ### based on the three criteria to select the action 408 | 409 | def change_untagged_vlan( this, xml ) 410 | proc = @@untgv_jmptbl[this.is_trunk?][this.should_trunk?][this.resource[:untagged_vlan].empty?] 411 | proc.call( this, xml ) 412 | end 413 | 414 | ### ------------------------------------------------------------- 415 | ### The following are all the change transition functions for 416 | ### each of the use-cases 417 | ### ------------------------------------------------------------- 418 | 419 | def ac_ac_nountg( this, xml ) 420 | NetdevJunos::Log.debug "ac_ac_nountg" 421 | # @@@ a port *MUST* be assigned to a vlan in access mode on MX. 422 | # @@@ generate an error! 423 | Kernel.raise Puppet::DevError, "untagged_vlan missing, port must be assigned to a VLAN" 424 | end 425 | 426 | def ac_tr_nountg( this, xml ) 427 | NetdevJunos::Log.debug "ac_tr_nountg" 428 | ## no action needed; handled already 429 | end 430 | 431 | def tr_ac_nountg( this, xml ) 432 | NetdevJunos::Log.debug "tr_ac_nountg" 433 | # @@@ a port *MUST* be assigned to a vlan in access mode on MX. 434 | # @@@ generate an error! 435 | Kernel.raise Puppet::DevError, "untagged_vlan missing, port must be assigned to a VLAN" 436 | end 437 | 438 | def tr_tr_nountg( this, xml ) 439 | NetdevJunos::Log.debug "tr_tr_nountg" 440 | set_native_vlan_id( this, xml, :delete ) 441 | end 442 | 443 | def ac_ac_untg( this, xml ) 444 | NetdevJunos::Log.debug "ac_ac_untg" 445 | set_vlan_id( this, xml ) 446 | end 447 | 448 | def ac_tr_untg( this, xml ) 449 | NetdevJunos::Log.debug "ac_tr_untg" 450 | set_vlan_id( this, xml, :delete ) if this.ndev_res[:untagged_vlan] 451 | set_native_vlan_id( this, xml ) 452 | end 453 | 454 | def tr_ac_untg( this, xml ) 455 | NetdevJunos::Log.debug "tr_ac_untg" 456 | set_native_vlan_id( this, xml, :delete ) 457 | set_vlan_id( this, xml ) 458 | end 459 | 460 | def tr_tr_untg( this, xml ) 461 | NetdevJunos::Log.debug "tr_tr_untg" 462 | set_native_vlan_id( this, xml ) 463 | end 464 | 465 | ### ------------------------------------------------------------- 466 | ### helper methods, re-usable for each of the transitions 467 | ### ------------------------------------------------------------- 468 | 469 | def set_vlan_id( this, xml, delete = :no ) 470 | vlan_id = vlan_names_to_tags( this.resource[:untagged_vlan] ) 471 | if delete == :delete 472 | xml.send( :'vlan-id', Netconf::JunosConfig::DELETE ) 473 | else 474 | xml.send( :'vlan-id', vlan_id ) 475 | end 476 | end 477 | 478 | def set_native_vlan_id( this, xml, delete = :no ) 479 | par = xml.instance_variable_get(:@parent) 480 | vlan_id = vlan_names_to_tags( this.resource[:untagged_vlan] ) 481 | 482 | Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::interface' )) do |dot| 483 | if delete == :delete 484 | # vlan-id-list is removed by another routine :-) 485 | dot.send( :'native-vlan-id', Netconf::JunosConfig::DELETE ) 486 | else 487 | xml.send( :'vlan-id-list', vlan_id ) 488 | dot.send( :'native-vlan-id', vlan_id ) 489 | end 490 | end 491 | 492 | end 493 | 494 | def set_ifd_trunking( xml, should_trunk ) 495 | par = xml.instance_variable_get(:@parent) 496 | Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::interface' )) do |dot| 497 | if should_trunk 498 | dot.send( :'flexible-vlan-tagging' ) 499 | dot.send( :'encapsulation', 'flexible-ethernet-services' ) 500 | else 501 | dot.send( :'flexible-vlan-tagging', Netconf::JunosConfig::DELETE ) 502 | dot.send( :'encapsulation', Netconf::JunosConfig::DELETE ) 503 | end 504 | end 505 | end 506 | 507 | end # class methods for changing untagged_vlan 508 | 509 | end 510 | 511 | 512 | --------------------------------------------------------------------------------