├── .mdlrc ├── .gitignore ├── Gemfile ├── features └── support │ └── env.rb ├── .mdl-style.rb ├── lib └── net │ ├── netconf │ ├── version.rb │ ├── jnpr.rb │ ├── jnpr │ │ ├── ioproc.rb │ │ ├── serial.rb │ │ ├── telnet.rb │ │ ├── ssh.rb │ │ ├── junos_config.rb │ │ └── rpc.rb │ ├── exception.rb │ ├── ioproc.rb │ ├── telnet.rb │ ├── rpc.rb │ ├── serial.rb │ ├── ssh.rb │ ├── rpc_std.rb │ └── transport.rb │ └── netconf.rb ├── .travis.yml ├── .codeclimate.yml ├── Rakefile ├── examples ├── jnpr │ ├── get_inventory.rb │ ├── get-inventory-telnet.rb │ ├── get_inventory_serial.rb │ ├── get_config_matching.rb │ ├── scp.rb │ ├── get-inventory-serial-explicit.rb │ ├── edit_config_jnpr_set.rb │ ├── edit_config_jnpr_text.rb │ ├── edit_config_std.rb │ ├── edit_config_text_std.rb │ ├── get_config_std.rb │ ├── edit_config_jnpr.rb │ └── get_config_jnpr.rb └── confd │ └── get_running.rb ├── LICENSE.txt ├── netconf.gemspec ├── CHANGELOG.md ├── README.md └── .rubocop.yml /.mdlrc: -------------------------------------------------------------------------------- 1 | style "#{Dir.pwd}/.mdl-style.rb" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | gems 3 | coverage/** -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require "simplecov" 2 | SimpleCov.start 3 | 4 | require_relative '../../lib/net/netconf' -------------------------------------------------------------------------------- /.mdl-style.rb: -------------------------------------------------------------------------------- 1 | all 2 | exclude_rule 'MD013' 3 | exclude_rule 'MD022' 4 | exclude_rule 'MD024' 5 | exclude_rule 'MD032' -------------------------------------------------------------------------------- /lib/net/netconf/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Netconf 4 | VERSION = '0.3.1' 5 | end 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1 4 | - 2.2 5 | - 2.3 6 | addons: 7 | code_climate: 8 | repo_token: e85ef2f27e0baec928bf94147cb2b8219006b7d02325d375041150879226400a 9 | after_success: 10 | - bundle exec codeclimate-test-reporter 11 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf' 4 | require 'net/netconf/jnpr/rpc' 5 | require 'net/netconf/jnpr/junos_config' 6 | 7 | module Netconf 8 | module Junos 9 | NETCONF_CLI = 'junoscript netconf need-trailer' 10 | NETCONF_SHELL = 'exec xml-mode netconf need-trailer' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/ioproc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf' 4 | require 'net/netconf/ioproc' 5 | require 'net/netconf/jnpr' 6 | 7 | module Netconf 8 | module Junos 9 | module IOProc 10 | def trans_open 11 | @trans = IO.popen('xml-mode netconf need-trailer', 'r+') 12 | self 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | bundler-audit: 4 | enabled: true 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - ruby 10 | fixme: 11 | enabled: true 12 | rubocop: 13 | enabled: true 14 | markdownlint: 15 | enabled: true 16 | ratings: 17 | paths: 18 | - Gemfile.lock 19 | - "**.rb" 20 | - "**.md" 21 | exclude_paths: 22 | - features/ 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'cucumber' 3 | require 'cucumber/rake/task' 4 | 5 | Cucumber::Rake::Task.new(:features) do |t| 6 | t.cucumber_opts = "--format pretty" 7 | end 8 | 9 | task default: :features 10 | rescue LoadError 11 | desc 'Cucumber rake task not available' 12 | task :features do 13 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 14 | end 15 | end -------------------------------------------------------------------------------- /examples/jnpr/get_inventory.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf' 2 | 3 | puts "NETCONF v#{Netconf::VERSION}" 4 | 5 | login = { 6 | target: 'vsrx', 7 | username: 'jeremy', 8 | password: 'jeremy1' 9 | } 10 | 11 | Netconf::SSH.new(login) do |dev| 12 | inv = dev.rpc.get_chassis_inventory 13 | 14 | puts 'Chassis: ' + inv.xpath('chassis/description').text 15 | puts 'Chassis Serial-Number: ' + inv.xpath('chassis/serial-number').text 16 | end 17 | -------------------------------------------------------------------------------- /examples/jnpr/get-inventory-telnet.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr/telnet' 2 | 3 | puts "NETCONF v#{Netconf::VERSION}" 4 | 5 | login = { :target => 'vsrx', :username => "jeremy", :password => "jeremy1" } 6 | 7 | Netconf::Telnet.new( login ){ |dev| 8 | inv = dev.rpc.get_chassis_inventory 9 | puts "Chassis: " + inv.xpath('chassis/description').text 10 | puts "Chassis Serial-Number: " + inv.xpath('chassis/serial-number').text 11 | 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/serial.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf' 4 | require 'net/netconf/serial' 5 | require 'net/netconf/jnpr' 6 | 7 | module Netconf 8 | module Junos 9 | module TransSerial 10 | def trans_start_netconf(last_console) 11 | last_console.match(/[^%]\s+$/) 12 | netconf_cmd = $1 == '%' ? Netconf::Junos::NETCONF_SHELL : Netconf::Junos::NETCONF_CLI 13 | puts netconf_cmd 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | # <******************* 2 | # 3 | # Copyright 2017 Juniper Networks, Inc. All rights reserved. 4 | # Licensed under the Juniper Networks Script Software License (the "License"). 5 | # You may not use this script file except in compliance with the License, which is located at 6 | # http://www.juniper.net/support/legal/scriptlicense/ 7 | # Unless required by applicable law or otherwise agreed to in writing by the parties, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # 10 | # *******************> -------------------------------------------------------------------------------- /examples/jnpr/get_inventory_serial.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr/serial' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | serial_port = '/dev/ttyS4' 6 | 7 | login = { 8 | port: serial_port, 9 | username: 'jeremy', 10 | password: 'jeremy1' 11 | } 12 | 13 | puts "Connecting to SERIAL: #{serial_port} ... please wait." 14 | 15 | Netconf::Serial.new(login) do |dev| 16 | puts 'Connected.' 17 | puts 'Nabbing Inventory ...' 18 | 19 | inv = dev.rpc.get_chassis_inventory 20 | 21 | puts 'Chassis: ' + inv.xpath('chassis/description').text 22 | puts 'Chassis Serial-Number: ' + inv.xpath('chassis/serial-number').text 23 | end 24 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/telnet.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf' 4 | require 'net/netconf/telnet' 5 | require 'net/netconf/jnpr' 6 | 7 | module Netconf 8 | module Junos 9 | module TransTelnet 10 | def trans_login 11 | l_rsp = @trans.login(@args[:username], @args[:password]) 12 | # @@@/JLS: need to rescue the timeout ... ??? 13 | l_rsp.match("([>%])\s+$") 14 | @exec_netconf = $1 == '%' ? Netconf::Junos::NETCONF_SHELL : Netconf::Junos::NETCONF_CLI 15 | end 16 | 17 | def trans_start_netconf 18 | @trans.puts @exec_netconf 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /examples/confd/get_running.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # This code is used to retrieve the running configuration 5 | # from a Tail-F "confD" NETCONF server, and display the 6 | # configured user names 7 | # 8 | 9 | require 'net/netconf' 10 | 11 | puts "NETCONF v#{Netconf::VERSION}" 12 | 13 | login = { 14 | target: 'jeap', 15 | port: 2022, 16 | username: 'admin', 17 | password: 'admin' 18 | } 19 | 20 | Netconf::SSH.new(login) do |dev| 21 | config = dev.rpc.get_config 22 | 23 | puts 'Showing users on this device ...' 24 | config.xpath('//users/user').each do |user| 25 | puts "Username: #{user.xpath('name').text}" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /examples/jnpr/get_config_matching.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | login = { 6 | target: 'vsrx', 7 | username: 'jeremy', 8 | password: 'jeremy1' 9 | } 10 | 11 | Netconf::SSH.new(login) do |dev| 12 | configs = dev.rpc.get_configuration do |x| 13 | x.interfaces(matching: 'interface[name="ge-*"]') 14 | end 15 | 16 | ge_cfgs = configs.xpath('interfaces/interface') 17 | 18 | puts "There are #{ge_cfgs.count} GE interfaces:" 19 | ge_cfgs.each do |ifd| 20 | units = ifd.xpath('unit').count 21 | puts ' ' + ifd.xpath('name')[0].text + " with #{units} unit" + (units > 1) ? 's' : '' 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /examples/jnpr/scp.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf' 2 | require 'net/scp' 3 | 4 | login = { 5 | target: 'vsrx', 6 | username: 'jeremy', 7 | password: 'jeremy1' 8 | } 9 | 10 | file_name = __FILE__ 11 | 12 | Netconf::SSH.new(login) do |dev| 13 | inv = dev.rpc.get_chassis_inventory 14 | 15 | puts 'Chassis: ' + inv.xpath('chassis/description').text 16 | puts 'Chassis Serial-Number: ' + inv.xpath('chassis/serial-number').text 17 | 18 | puts "Copying file #{file_name} to home directory ..." 19 | dev.scp.upload! file_name, file_name 20 | 21 | puts 'Copying latest config file from target to local machine ...' 22 | 23 | dev.scp.download! '/config/juniper.conf.gz', '/var/tmp/config.gz' 24 | end 25 | -------------------------------------------------------------------------------- /examples/jnpr/get-inventory-serial-explicit.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr/serial' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | login = { 6 | :port => '/dev/ttyS4', 7 | :username => "root", :password => "juniper1" 8 | } 9 | 10 | # we want to mount the USB drive, so we need to explicity 11 | # do something special when opening the serial console ... 12 | # therefore, we can *NOT* pass a block directly to new() 13 | 14 | dev = Netconf::Serial.new( login ) 15 | dev.open { |con| 16 | # login has occurred successfully 17 | 18 | con.puts 'mount_msdosfs /dev/da1s1 /mnt' 19 | 20 | # netconf will be started once block completes 21 | } 22 | 23 | inv = dev.rpc.get_chassis_inventory 24 | 25 | dev.close 26 | 27 | 28 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/ssh.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf' 4 | require 'net/netconf/jnpr' 5 | 6 | module Netconf 7 | module Junos 8 | # this is used to handle the case where NETCONF (port 830) is disabled. 9 | # We can still access the NETCONF subsystem from the CLI using a hidden 10 | # command 'netconf' 11 | module TransSSH 12 | def trans_on_connect_refused(start_args) 13 | start_args[:port] = 22 14 | @trans[:conn] = Net::SSH.start(@args[:target], @args[:username], start_args) 15 | do_once = true 16 | @trans[:conn].exec(NETCONF_CLI) do |chan, _success| 17 | @trans[:chan] = chan 18 | do_once = false 19 | end 20 | @trans[:conn].loop { do_once } 21 | @trans[:chan] 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/net/netconf/exception.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Netconf 4 | class InitError < StandardError 5 | end 6 | 7 | class StateError < StandardError 8 | end 9 | 10 | class OpenError < StandardError 11 | end 12 | 13 | # returns RPC and error message 14 | class RpcError < StandardError 15 | attr_reader :trans 16 | attr_reader :cmd, :rsp 17 | 18 | def initialize(trans, cmd, rsp) 19 | @trans = trans 20 | @cmd = cmd 21 | @rsp = rsp 22 | end 23 | 24 | def to_s 25 | "RPC command error: #{cmd.first_element_child.name}\n#{rsp.to_xml}" 26 | end 27 | end 28 | 29 | class EditError < Netconf::RpcError 30 | end 31 | 32 | class LockError < Netconf::RpcError 33 | end 34 | 35 | class CommitError < Netconf::RpcError 36 | end 37 | 38 | class ValidateError < Netconf::RpcError 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /netconf.gemspec: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift 'lib' 2 | require 'rake' 3 | require 'net/netconf/version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'netconf' 7 | s.licenses = ['JSSL'] 8 | s.version = Netconf::VERSION 9 | s.summary = "NETCONF client" 10 | s.description = "Extensible Ruby-based NETCONF client with native support for Junos. Maintained by Juniper Networks" 11 | s.homepage = 'https://github.com/Juniper/net-netconf' 12 | s.authors = ["Jeremy Schulman", "Ankit Jain", "David Gethings"] 13 | s.email = 'dgjnpr@gmail.com' 14 | s.files = FileList['lib/net/**/*.rb', 'examples/**/*.rb'] 15 | s.required_ruby_version = '>= 2.1.0' 16 | s.add_runtime_dependency('nokogiri', '~> 1.7') 17 | s.add_runtime_dependency('net-ssh', '~> 4.1') 18 | s.add_runtime_dependency('net-scp', '~> 1.2') 19 | s.add_development_dependency('rake', '~> 12.0') 20 | s.add_development_dependency('simplecov', '~> 0.14') 21 | s.add_development_dependency('codeclimate-test-reporter', '~> 1.0') 22 | # s.add_development_dependency('rspec-core', '~> 3.5') 23 | # s.add_development_dependency('rspec-expectations', '~> 3.5') 24 | s.add_development_dependency('cucumber', '~> 2.4') 25 | s.add_development_dependency('rubocop', '~> 0.48') 26 | end 27 | -------------------------------------------------------------------------------- /lib/net/netconf/ioproc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Netconf 4 | class IOProc < Netconf::Transport 5 | DEFAULT_RDBLKSZ = (1024 * 1024) 6 | 7 | attr_reader :args 8 | 9 | def initialize(args_h = {}, &block) 10 | os_type = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE 11 | 12 | @args = args_h.clone 13 | 14 | # an OS specific implementation must exist to support this transport type 15 | extend Netconf::const_get(os_type)::IOProc 16 | 17 | @trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT 18 | @trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO 19 | 20 | super(&block) 21 | end 22 | 23 | # the OS specific transport must implement this method 24 | def trans_open # :yield: self 25 | raise 'Unsupported IOProc' 26 | end 27 | 28 | def trans_receive_hello 29 | trans_receive 30 | end 31 | 32 | def trans_send_hello 33 | nil 34 | end 35 | 36 | def trans_close 37 | @trans.write Netconf::RPC::MSG_CLOSE_SESSION 38 | @trans.close 39 | end 40 | 41 | def trans_send(cmd_str) 42 | @trans.write(cmd_str) 43 | end 44 | 45 | def trans_receive 46 | Netconf.trans_receive 47 | end 48 | 49 | def puts(str = nil) 50 | @trans.puts(str) 51 | end 52 | 53 | def waitfor(on_re) 54 | Netconf.waitfor(on_re) 55 | end 56 | end # class: IOProc 57 | end # module: Netconf 58 | -------------------------------------------------------------------------------- /lib/net/netconf/telnet.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/telnet' 4 | 5 | module Netconf 6 | class Telnet < Netconf::Transport 7 | def initialize(args, _trans_args = nil, &block) 8 | os_type = args[:os_type] || Netconf::DEFAULT_OS_TYPE 9 | @args = args.clone 10 | 11 | # extend this instance with the capabilities of the specific console 12 | # type; it needs to define #login and #start_netconf session 13 | begin 14 | extend Netconf.const_get(os_type).TransTelnet 15 | rescue NameError 16 | # no extensions available ... 17 | end 18 | 19 | my_trans_args = {} 20 | my_trans_args['Host'] = @args[:target] 21 | my_trans_args['Port'] = @args[:port] if @args[:port] 22 | 23 | @trans = Net::Telnet.new(my_trans_args) 24 | 25 | @trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT 26 | @trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO 27 | 28 | super(&block) 29 | end 30 | 31 | def trans_open(&block) 32 | trans_login 33 | trans_start_netconf 34 | self 35 | end 36 | 37 | def trans_close 38 | @trans.write Netconf::RPC::MSG_CLOSE_SESSION 39 | @trans.close 40 | end 41 | 42 | def trans_send(cmd_str) 43 | @trans.write(cmd_str) 44 | end 45 | 46 | def trans_receive 47 | rsp = @trans.waitfor(Netconf::RPC::MSG_END_RE) 48 | rsp.chomp!(Netconf::RPC::MSG_END + "\n") 49 | end 50 | end # class: Serial 51 | end # module: Netconf 52 | -------------------------------------------------------------------------------- /examples/jnpr/edit_config_jnpr_set.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | login = { 6 | target: 'vsrx', 7 | username: 'jeremy', 8 | password: 'jeremy1' 9 | } 10 | 11 | new_host_name = 'vsrx' 12 | 13 | puts "Connecting to device: #{login[:target]}" 14 | 15 | Netconf::SSH.new(login) do |dev| 16 | puts 'Connected!' 17 | 18 | location = [] 19 | location << 'set system location building "Main Campus, C"' 20 | location << 'set system location floor 15' 21 | location << 'set system location rack 1117' 22 | 23 | begin 24 | dev.rpc.lock_configuration 25 | 26 | # -------------------------------------------------------------------- 27 | # configuration as BLOCK 28 | 29 | dev.rpc.load_configuration(format: 'set') do 30 | "set system host-name #{new_host_name}" 31 | end 32 | 33 | # -------------------------------------------------------------------- 34 | # configuration as PARAM 35 | 36 | dev.rpc.load_configuration(location, format: 'set') 37 | 38 | dev.rpc.check_configuration 39 | dev.rpc.commit_configuration 40 | dev.rpc.unlock_configuration 41 | 42 | rescue Netconf::LockError 43 | puts 'Lock error' 44 | rescue Netconf::EditError 45 | puts 'Edit error' 46 | rescue Netconf::ValidateError 47 | puts 'Validate error' 48 | rescue Netconf::CommitError 49 | puts 'Commit error' 50 | rescue Netconf::RpcError 51 | puts 'General RPC error' 52 | else 53 | puts 'Configuration Committed.' 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /examples/jnpr/edit_config_jnpr_text.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | login = { 6 | target: 'vsrx', 7 | username: 'jeremy', 8 | password: 'jeremy1' 9 | } 10 | 11 | new_host_name = 'vsrx-gizmo' 12 | 13 | puts "Connecting to device: #{login[:target]}" 14 | 15 | Netconf::SSH.new(login) do |dev| 16 | puts ' Connected!' 17 | 18 | location = < 25 | puts cfgall.xpath('configuration/system') 26 | 27 | # ---------------------------------------------------------------------- 28 | # specifying a filter as a block to get_config 29 | 30 | cfgsvc1 = dev.rpc.get_config do |x| 31 | x.configuration { x.system { x.services } } 32 | end 33 | 34 | puts 'Retrieved services as BLOCK:' 35 | cfgsvc1.xpath('//services/*').each { |s| puts s.name } 36 | 37 | # ---------------------------------------------------------------------- 38 | # specifying a filter as a parameter to get_config 39 | 40 | filter = Nokogiri::XML::Builder.new do |x| 41 | x.configuration { x.system { x.services } } 42 | end 43 | 44 | cfgsvc2 = dev.rpc.get_config(filter) 45 | puts 'Retrieved services as PARAM:' 46 | cfgsvc2.xpath('//services/*').each { |s| puts s.name } 47 | 48 | cfgsvc3 = dev.rpc.get_config(filter) 49 | puts 'Retrieved services as PARAM, re-used filter' 50 | cfgsvc3.xpath('//services/*').each { |s| puts s.name } 51 | end 52 | -------------------------------------------------------------------------------- /lib/net/netconf.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'nokogiri' 4 | require 'net/netconf/version' 5 | require 'net/netconf/rpc' 6 | require 'net/netconf/exception' 7 | require 'net/netconf/transport' 8 | require 'net/netconf/ssh' 9 | 10 | # base Netconf constants and methods 11 | module Netconf 12 | NAMESPACE = 'urn:ietf:params:xml:ns:netconf:base:1.0' 13 | DEFAULT_OS_TYPE = :Junos 14 | DEFAULT_TIMEOUT = 10 15 | DEFAULT_WAITIO = 0 16 | 17 | # do not raise RpcError exception when rpc returns a warning 18 | @raise_on_warning = false 19 | 20 | def self.raise_on_warning=(bool) 21 | @raise_on_warning = bool 22 | end 23 | 24 | def self.raise_on_warning 25 | @raise_on_warning 26 | end 27 | 28 | def self.waitfor(on_re = nil) 29 | time_out = @trans_timeout 30 | wait_io = @trans_waitio 31 | 32 | time_out = nil if time_out == false 33 | done = false 34 | rx_buf = '' 35 | 36 | until( rx_buf.match( on_re ) and not IO::select( [@trans], nil, nil, wait_io ) ) 37 | 38 | unless IO::select( [@trans], nil, nil, time_out ) 39 | raise TimeoutError, 'Netconf IO timed out while waiting for more data' 40 | end 41 | 42 | begin 43 | 44 | rx_some = @trans.readpartial( DEFAULT_RDBLKSZ ) 45 | 46 | rx_buf += rx_some 47 | break if rx_buf.match( on_re ) 48 | 49 | rescue EOFError # End of file reached 50 | rx_buf = nil if rx_buf == '' 51 | break # out of outer 'until' loop 52 | end 53 | 54 | end 55 | rx_buf 56 | end 57 | 58 | def self.trans_receive 59 | got = waitfor( Netconf::RPC::MSG_END_RE ) 60 | msg_end = got.rindex( Netconf::RPC::MSG_END ) 61 | got[msg_end .. -1] = '' 62 | got 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /examples/jnpr/edit_config_jnpr.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf/jnpr' 4 | 5 | puts "NETCONF v#{Netconf::VERSION}" 6 | 7 | login = { 8 | target: 'ex4', 9 | username: 'jeremy', 10 | password: 'jeremy1' 11 | } 12 | 13 | new_host_name = 'ex4-abc' 14 | 15 | puts "Connecting to device: #{login[:target]}" 16 | 17 | Netconf::SSH.new(login) do |dev| 18 | puts 'Connected!' 19 | # when providing a collection of configuration, 20 | # you need to include the as the 21 | # toplevel element 22 | location = Nokogiri::XML::Builder.new do |x| 23 | x.configuration do 24 | x.system do 25 | x.location do 26 | x.building 'Main Campus, D' 27 | x.floor 22 28 | x.rack 38 29 | end 30 | end 31 | x.system do 32 | x.services do 33 | x.ftp 34 | end 35 | end 36 | end 37 | end 38 | 39 | begin 40 | dev.rpc.lock_configuration 41 | 42 | # -------------------------------------------------------------------- 43 | # configuration as PARAM 44 | 45 | dev.rpc.load_configuration(location, action: 'replace') 46 | 47 | # -------------------------------------------------------------------- 48 | # configuration as BLOCK 49 | 50 | dev.rpc.load_configuration do |x| 51 | x.system do 52 | x.send(:'host-name', new_host_name) 53 | end 54 | end 55 | 56 | dev.rpc.check_configuration 57 | dev.rpc.commit_configuration 58 | dev.rpc.unlock_configuration 59 | rescue Netconf::LockError 60 | puts 'Lock error' 61 | rescue Netconf::EditError 62 | puts 'Edit error' 63 | rescue Netconf::ValidateError 64 | puts 'Validate error' 65 | rescue Netconf::CommitError 66 | puts 'Commit error' 67 | rescue Netconf::RpcError 68 | puts 'General RPC error' 69 | else 70 | puts 'Configuration Committed.' 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /examples/jnpr/get_config_jnpr.rb: -------------------------------------------------------------------------------- 1 | require 'net/netconf/jnpr' 2 | 3 | puts "NETCONF v.#{Netconf::VERSION}" 4 | 5 | login = { 6 | target: 'ex4', 7 | username: 'jeremy', 8 | password: 'jeremy1' 9 | } 10 | 11 | puts "Connecting to device: #{login[:target]}" 12 | 13 | Netconf::SSH.new(login) do |dev| 14 | puts 'Connected.' 15 | 16 | puts 'Retrieving full config, please wait ... ' 17 | # Junos specific RPC 18 | cfgall = dev.rpc.get_configuration 19 | puts "Showing 'system' hierarchy ..." 20 | # Root is , so don't need to include it in XPath 21 | puts cfgall.xpath('system') 22 | 23 | # ---------------------------------------------------------------------- 24 | # specifying a filter as a block to get_configuration 25 | # Junos extension does the proper toplevel wrapping 26 | 27 | puts 'Retrieved services from BLOCK, as XML:' 28 | 29 | cfgsvc1 = dev.rpc.get_configuration do |x| 30 | x.system { x.services } 31 | x.system { x.login } 32 | end 33 | 34 | cfgsvc1.xpath('system/services/*').each { |s| puts s.name } 35 | 36 | puts 'Retrieved services from BLOCK, as TEXT:' 37 | 38 | cfgsvc2 = dev.rpc.get_configuration(format: 'text') do |x| 39 | x.system { x.services } 40 | end 41 | 42 | puts cfgsvc2.text 43 | 44 | # ---------------------------------------------------------------------- 45 | # specifying a filter as a parameter to get_configuration 46 | # you must wrap the config in a toplevel element 47 | 48 | filter = Nokogiri::XML::Builder.new do |x| 49 | x.configuration do 50 | x.system { x.services } 51 | x.system { x.login } 52 | end 53 | end 54 | 55 | puts 'Retrieved services by PARAM, as XML' 56 | 57 | cfgsvc3 = dev.rpc.get_configuration(filter) 58 | cfgsvc3.xpath('system/services/*').each { |s| puts s.name } 59 | 60 | # ---------------------------------------------------------------------- 61 | # specifying a filter as a parameter to get_configuration, 62 | # get response back in "text" format 63 | 64 | puts 'Retrieved services by PARAM, as TEXT:' 65 | cfgsvc4 = dev.rpc.get_configuration(filter, format: 'text') 66 | puts cfgsvc4.text 67 | end 68 | -------------------------------------------------------------------------------- /lib/net/netconf/rpc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/netconf/rpc_std' 4 | 5 | module Netconf 6 | module RPC 7 | def self.add_attributes(ele_nx, attr_h) 8 | attr_h.each { |k, v| ele_nx[k] = v } 9 | end 10 | 11 | def self.set_exception(rpc_nx, exception) 12 | rpc_nx.instance_variable_set(:@netconf_exception, exception) 13 | end 14 | 15 | def self.get_exception(rpc_nx) 16 | rpc_nx.instance_variable_get(:@netconf_exception) || Netconf::RpcError 17 | end 18 | 19 | module Builder 20 | # autogenerate an , converting underscores (_) 21 | # to hyphens (-) along the way ... 22 | 23 | def self.method_missing(method, params = nil, attrs = nil) 24 | rpc_name = method.to_s.tr('_', '-').to_sym 25 | 26 | # build the XML starting at , envelope the 27 | # toplevel element, then create name/value elements for each 28 | # of the additional params. An element without a value should 29 | # simply be set to true 30 | rpc_nx = if params 31 | Nokogiri::XML::Builder.new do |xml| 32 | xml.rpc do 33 | xml.send(rpc_name) do 34 | params.each do |k, v| 35 | sym = k.to_s.tr('_', '-').to_sym 36 | xml.send(sym, v == true ? nil : v) 37 | end 38 | end 39 | end 40 | end.doc.root 41 | else 42 | # -- no params 43 | Nokogiri::XML("<#{rpc_name}/>").root 44 | end 45 | 46 | # if a block is given it is used to set the attributes of the 47 | # toplevel element 48 | add_attributes(rpc_nx.at(rpc_name), attrs) if attrs 49 | 50 | # return the rpc command 51 | rpc_nx 52 | end # def: method-missing? 53 | 54 | end # module: Builder 55 | 56 | class Executor 57 | include Netconf::RPC::Standard 58 | 59 | def initialize(trans, os_type) 60 | @trans = trans 61 | begin 62 | extend Netconf::RPC::const_get(os_type) 63 | rescue NameError 64 | # no extensions available ... 65 | end 66 | end 67 | 68 | def method_missing(method, params = nil, attrs = nil) 69 | @trans.rpc_exec(Netconf::RPC::Builder.send(method, params, attrs)) 70 | end 71 | end # class: Executor 72 | end # module: RPC 73 | end # module: Netconf 74 | -------------------------------------------------------------------------------- /lib/net/netconf/serial.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'serialport' 4 | 5 | module Netconf 6 | class Serial < Netconf::Transport 7 | DEFAULT_BAUD = 9600 8 | DEFAULT_DATABITS = 8 9 | DEFAULT_STOPBITS = 1 10 | DEFAULT_PARITY = SerialPort::NONE 11 | DEFAULT_RDBLKSZ = (1024 * 1024) 12 | 13 | attr_reader :args 14 | 15 | def initialize(args_h, &block) 16 | os_type = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE 17 | 18 | raise Netconf::InitError, "Missing 'port' param" unless args_h[:port] 19 | raise Netconf::InitError, "Missing 'username' param" unless args_h[:username] 20 | 21 | @args = args_h.clone 22 | @args[:prompt] ||= /([%>])\s+$/ 23 | 24 | # extend this instance with the capabilities of the specific console 25 | # type; it needs to define #trans_start_netconf session 26 | # this must be provided! if the caller does not, this will 27 | # throw a NameError exception. 28 | 29 | extend Netconf::const_get(os_type)::TransSerial 30 | 31 | @trans_timeout = @args[:timeout] || Netconf::DEFAULT_TIMEOUT 32 | @trans_waitio = @args[:waitio] || Netconf::DEFAULT_WAITIO 33 | 34 | super(&block) 35 | end 36 | 37 | def login 38 | begin 39 | puts 40 | waitfor(/ogin:/) 41 | rescue Timeout::Error 42 | puts 43 | waitfor(/ogin:/) 44 | end 45 | 46 | puts @args[:username] 47 | 48 | waitfor(/assword:/) 49 | puts @args[:password] 50 | 51 | waitfor(@args[:prompt]) 52 | end 53 | 54 | def trans_open # :yield: self 55 | baud = @args[:speed] || DEFAULT_BAUD 56 | data_bits = @args[:bits] || DEFAULT_DATABITS 57 | stop_bits = @args[:stop] || DEFAULT_STOPBITS 58 | parity = @args[:parity] || DEFAULT_PARITY 59 | 60 | @trans = SerialPort.new(@args[:port], baud, data_bits, stop_bits, parity) 61 | 62 | got = login 63 | yield self if block_given? 64 | trans_start_netconf(got) 65 | 66 | self 67 | end 68 | 69 | def trans_receive_hello 70 | hello_str = trans_receive 71 | so_xml = hello_str.index("\n") + 1 72 | hello_str.slice!(0, so_xml) 73 | hello_str 74 | end 75 | 76 | def trans_send_hello 77 | nil 78 | end 79 | 80 | def trans_close 81 | @trans.write Netconf::RPC::MSG_CLOSE_SESSION 82 | @trans.close 83 | end 84 | 85 | def trans_send(cmd_str) 86 | @trans.write(cmd_str) 87 | end 88 | 89 | def trans_receive 90 | got = waitfor(Netconf::RPC::MSG_END_RE) 91 | msg_end = got.rindex(Netconf::RPC::MSG_END) 92 | got[msg_end..-1] = '' 93 | got 94 | end 95 | 96 | def puts(str = nil) 97 | @trans.puts str 98 | end 99 | 100 | def waitfor(this_re = nil) 101 | Netconf.waitfor(on_re) 102 | end 103 | end # class: Serial 104 | end # module: Netconf 105 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/junos_config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # 4 | # Copyright (c) 2012 Juniper Networks, Inc. 5 | # All Rights Reserved 6 | # 7 | # JUNIPER PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 8 | 9 | module Netconf 10 | class JunosConfig 11 | DELETE = { delete: 'delete' }.freeze 12 | REPLACE = { replace: 'replace' }.freeze 13 | 14 | attr_reader :doc 15 | attr_reader :collection 16 | 17 | def initialize(options) 18 | @doc_ele = 'configuration' 19 | 20 | if options == :TOP 21 | @doc = Nokogiri::XML("<#{@doc_ele}/>") 22 | return 23 | end 24 | 25 | unless options[:TOP].nil? 26 | @doc_ele = options[:TOP] 27 | @doc = Nokogiri::XML("<#{@doc_ele}/>") 28 | return 29 | end 30 | 31 | unless defined? @collection 32 | edit = "#{@doc_ele}/#{options[:edit].strip}" 33 | @at_name = edit[edit.rindex('/') + 1, edit.length] 34 | @edit_path = edit 35 | @collection = {} 36 | @to_xml = options[:build] 37 | end 38 | end 39 | 40 | def <<(obj) 41 | if defined? @collection 42 | @collection[obj[:name]] = obj 43 | elsif defined? @doc 44 | obj.build_xml(@doc) 45 | else 46 | # TBD:error 47 | end 48 | end 49 | 50 | def build_xml(ng_xml, &block) 51 | at_ele = ng_xml.at(@edit_path) 52 | if at_ele.nil? 53 | # no xpath anchor point, so we need to create it 54 | at_ele = edit_path(ng_xml, @edit_path) 55 | end 56 | build_proc = block_given? ? block : @to_xml 57 | 58 | @collection.each do |_k, v| 59 | with(at_ele) do |e| 60 | build_proc.call(e, v) 61 | end 62 | end 63 | end 64 | 65 | def edit_path(ng_xml, xpath) 66 | # junos configuration always begins with 67 | # the 'configuration' element, so don't make 68 | # the user enter it all the time 69 | 70 | cfg_xpath = xpath 71 | dot = ng_xml.at(cfg_xpath) 72 | return dot if dot 73 | 74 | # we need to determine how much of the xpath 75 | # we need to create. walk down the xpath 76 | # children to determine what exists and 77 | # what needs to be added 78 | 79 | xpath_a = cfg_xpath.split('/') 80 | need_a = [] 81 | until xpath_a.empty? || dot 82 | need_a.unshift xpath_a.pop 83 | check_xpath = xpath_a.join('/') 84 | dot = ng_xml.at(check_xpath) 85 | end 86 | 87 | # start at the deepest level of what 88 | # actually exists and then start adding 89 | # the children that were missing 90 | 91 | dot = ng_xml.at(xpath_a.join('/')) 92 | need_a.each do |ele| 93 | dot = dot.add_child(Nokogiri::XML::Node.new(ele, ng_xml)) 94 | end 95 | dot 96 | end 97 | 98 | def with(ng_xml, &block) 99 | Nokogiri::XML::Builder.with(ng_xml, &block) 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/net/netconf/ssh.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/ssh' 4 | require 'net/netconf/ssh' 5 | 6 | module Netconf 7 | class SSH < Netconf::Transport 8 | NETCONF_PORT = 830 9 | NETCONF_SUBSYSTEM = 'netconf' 10 | 11 | def initialize(args_h, &block) 12 | @args = args_h.clone 13 | @args[:os_type] = args_h[:os_type] || Netconf::DEFAULT_OS_TYPE 14 | 15 | # extend this instance with the capabilities of the specific os_type 16 | begin 17 | extend Netconf.const_get(@args[:os_type]).TransSSH 18 | rescue NameError 19 | # no extensions available ... 20 | end 21 | 22 | @trans = {} 23 | super(&block) 24 | end 25 | 26 | def trans_open(&block) 27 | # open a connection to the NETCONF subsystem 28 | start_args = {} 29 | start_args[:password] ||= @args[:password] 30 | start_args[:passphrase] = @args[:passphrase] || nil 31 | start_args[:port] = @args[:port] || NETCONF_PORT 32 | start_args.merge!(@args[:ssh_args]) if @args[:ssh_args] 33 | 34 | begin 35 | @trans[:conn] = Net::SSH.start(@args[:target], @args[:username], start_args) 36 | @trans[:chan] = @trans[:conn].open_channel do |ch| 37 | ch.subsystem(NETCONF_SUBSYSTEM) 38 | end 39 | rescue Errno::ECONNREFUSED => e 40 | if self.respond_to? 'trans_on_connect_refused' 41 | return trans_on_connect_refused(start_args) 42 | end 43 | return nil 44 | end 45 | @trans[:chan] 46 | end 47 | 48 | def trans_close 49 | @trans[:chan].close if @trans[:chan] 50 | @trans[:conn].close if @trans[:conn] 51 | end 52 | 53 | def trans_receive 54 | @trans[:rx_buf] = '' 55 | @trans[:more] = true 56 | 57 | # collect the response data as it comes back ... 58 | # the "on" functions must be set before calling 59 | # the #loop method 60 | 61 | @trans[:chan].on_data do |_ch, data| 62 | if data.include?(RPC::MSG_END) 63 | data.slice!(RPC::MSG_END) 64 | @trans[:rx_buf] << data unless data.empty? 65 | @trans[:more] = false 66 | else 67 | @trans[:rx_buf] << data 68 | end 69 | end 70 | 71 | # ... if there are errors ... 72 | @trans[:chan].on_extended_data do |_ch, _type, data| 73 | @trans[:rx_err] = data 74 | @trans[:more] = false 75 | end 76 | 77 | # the #loop method is what actually performs 78 | # ssh event processing ... 79 | 80 | @trans[:conn].loop { @trans[:more] } 81 | 82 | @trans[:rx_buf] 83 | end 84 | 85 | def trans_send(cmd_str) 86 | @trans[:chan].send_data(cmd_str) 87 | end 88 | 89 | # accessor to create an Net::SCP object so the caller can perform 90 | # secure-copy operations (see Net::SCP) for details 91 | def scp 92 | @scp ||= Net::SCP.start(@args[:target], 93 | @args[:username], 94 | password: @args[:password], 95 | port: @args[:port] || 22) 96 | end 97 | end # class: SSH 98 | end # module: Netconf 99 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## 0.4, the wilderness years 8 | There is no 0.4.x release. Active support moved to the community during this time. Much kudos and love to kkirsche for keeping this project alive. As a sign of respect (and to avoid confusion) the only 0.4 releases will be the ones developed my kkirsche. 9 | 10 | Next stop: 0.5.0! 11 | 12 | ## 0.3.1, 2013-07-26 13 | ### Added 14 | - extension support to Netconf::SSH. For example, see net/netconf/jnpr/ssh.rb. 15 | - Juniper Netconf::SSH extension to access NETCONF subsystem via CLI command if NETCONF port (830) is not enabled. This enhancement was added for users that have Junos systems depoloyed, but didn't enable NETCONF. Note that this enhacement assumes that the user login starts at the standard Junos CLI (i.e. not root user) 16 | 17 | ## 0.3.0, 2013-07-21 18 | A number of pull requests were manually merged as a result of my learning curve around git. My sincere apologies, on the delay bringing these updates into the mainline. If I missed you on this list, please let me know and I'll update accordingly. Thx, Jeremy 19 | 20 | NOTE: If you intend to use `Netconf::Serial` you will need to ensure that the `serialport` gem is installed. This gem is not explicitly included in the gemspec 21 | 22 | ### Added 23 | - Netconf::RPC::MSG_END on each RPC. kudos: wpaulson 24 | - Juniper specific request_pfe_execute. kudos: dgjnpr 25 | - `:ssh_args` hash on Netconf::SSH to support any of the Net::SSH start args. kudos: imbracio 26 | - "deep" look for rpc-error tags rather than just the first two levels. kudos: jof 27 | - Netconf helper method `open?` and `closed?` for checking NETCONF session state. kudos: jof 28 | - net-scp to netconf.gemspec dependency. This gem is really only required if you intent to use the SCP functionality; but since this is turning out to be a common use-case, the gem has been added to the dependency list 29 | 30 | ### Changed 31 | - check for rpc-error severity='error' to handle the case where the rpc-error element is actually not an error, but rather severity='warning'. If the severity is in fact __not__ error, then the Netconf::RpcError __will not__ be generated. In Netconf::VERSION <= 0.2.5, the warnings would actually cause an exception. If you would like to maintain the older behavior, then you will need to set `Netconf::raise_on_warning = true`. kudos: jeremyschulman 32 | - netconf.gemspec to include only the `version.rb` file; also separated out the Netconf::VERSION into a separate file. kudos: request by multiple folks 33 | - Converted files from MS-DOS format to Unix; stripped out all of MS-DOS format kruft. kudos: jof 34 | 35 | ## 0.2.5, 2012-01-29 36 | ### Added 37 | - IOProc support 38 | 39 | ### Changed 40 | - Refactored code to enhance multi-vendor 41 | 42 | ### Fixed 43 | - Junos specific RPCs 44 | 45 | ## 0.2.4, 2012-01-16 46 | ### Added 47 | - `command` support for Junos RPC 48 | 49 | ## 0.2.2, 2012-01-14 50 | Tested against Tail-F "confD" server 51 | 52 | ### Added 53 | - RFC required `rpc` namespace and `message-id` attributes 54 | 55 | ## 0.2.1, 2012-01-09 56 | ### Added 57 | - support for Net::SCP accessor in SSH transport. See example code [scp.rb](examples/jnpr/scp.rb); you will need to explicity require 'net/scp' in your top-level code 58 | 59 | ## 0.2.0, 2012-01-06 60 | Tested against JUNOS devices 61 | 62 | ### Added 63 | - Support for SSH, Telnet, and Console 64 | -------------------------------------------------------------------------------- /lib/net/netconf/rpc_std.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Netconf 4 | module RPC 5 | MSG_END = ']]>]]>' 6 | MSG_END_RE = /\]\]>\]\]>[\r\n]*$/ 7 | MSG_CLOSE_SESSION = '' 8 | MSG_HELLO = <<-EOM.gsub(/\s+\|/, '') 9 | | 10 | | 11 | | urn:ietf:params:netconf:base:1.0 12 | | 13 | | 14 | EOM 15 | 16 | module Standard 17 | def lock(target) 18 | run_valid_or_lock_rpc("<#{target}/>", 19 | Netconf::LockError) 20 | 21 | end 22 | 23 | def unlock(target) 24 | rpc = Nokogiri::XML("<#{target}/>").root 25 | @trans.rpc_exec(rpc) 26 | end 27 | 28 | def validate(source) 29 | run_valid_or_lock_rpc("<#{source}/>", 30 | Netconf::ValidateError) 31 | 32 | end 33 | 34 | def run_valid_or_lock_rpc(rpc_string, error_type) 35 | rpc = Nokogiri::XML("#{rpc_string}").root 36 | Netconf::RPC.set_exception(rpc, error_type) 37 | @trans.rpc_exec(rpc) 38 | end 39 | 40 | def commit 41 | rpc = Nokogiri::XML('').root 42 | Netconf::RPC.set_exception(rpc, Netconf::CommitError) 43 | @trans.rpc_exec(rpc) 44 | end 45 | 46 | def delete_config(target) 47 | rpc = Nokogiri::XML("<#{target}/>").root 48 | @trans.rpc_exec(rpc) 49 | end 50 | 51 | def process_args(args) 52 | while arg = args.shift 53 | case arg.class.to_s 54 | when /^Nokogiri/ 55 | filter = case arg 56 | when Nokogiri::XML::Builder then arg.doc.root 57 | when Nokogiri::XML::Document then arg.root 58 | else arg 59 | end 60 | when 'Hash' then attrs = arg 61 | when 'String' then source = arg 62 | end 63 | end 64 | end 65 | 66 | def get_config(*args) # :yield: filter_builder 67 | source = 'running' # default source is 'running' 68 | filter = nil # no filter by default 69 | 70 | arg = process_args(args) 71 | 72 | rpc = Nokogiri::XML("<#{source}/>").root 73 | 74 | if block_given? 75 | Nokogiri::XML::Builder.with(rpc.at('get-config')) do |xml| 76 | xml.filter(type: 'subtree') { yield(xml) } 77 | end 78 | end 79 | 80 | if filter 81 | f_node = Nokogiri::XML::Node.new('filter', rpc) 82 | f_node['type'] = 'subtree' 83 | f_node << filter.dup 84 | rpc.at('get-config') << f_node 85 | end 86 | 87 | @trans.rpc_exec(rpc) 88 | end 89 | 90 | def edit_config(*args) # :yield: config_builder 91 | toplevel = 'config' # default toplevel config element 92 | target = 'candidate' # default source is 'candidate' @@@/JLS hack; need to fix this 93 | config = nil 94 | options = {} 95 | 96 | arg = process_args(args) 97 | toplevel = options[:toplevel] if options[:toplevel] 98 | 99 | rpc_str = <<-EO_RPC.gsub(/^\s*\|/, '') 100 | | 101 | | 102 | | <#{target}/> 103 | | <#{toplevel}/> 104 | | 105 | | 106 | EO_RPC 107 | 108 | rpc = Nokogiri::XML(rpc_str).root 109 | 110 | if block_given? 111 | Nokogiri::XML::Builder.with(rpc.at(toplevel)) { |xml| yield(xml) } 112 | elsif config 113 | rpc.at(toplevel) << config.dup 114 | else 115 | raise ArgumentError, 'You must specify edit-config data!' 116 | end 117 | 118 | Netconf::RPC.set_exception(rpc, Netconf::EditError) 119 | @trans.rpc_exec(rpc) 120 | end 121 | end 122 | end # module: RPC 123 | end # module: Netconf 124 | -------------------------------------------------------------------------------- /lib/net/netconf/transport.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ## ----------------------------------------------------------------------- 4 | ## This file contains the Netconf::Transport parent class definition. 5 | ## All other transports, i.e. "ssh", "serial", "telnet" use this parent 6 | ## class to define their transport specific methods: 7 | ## 8 | ## trans_open: open the transport connection 9 | ## trans_close: close the transport connection 10 | ## trans_send: send XML command (String) via transport 11 | ## trans_receive: receive XML response (String) via transport 12 | ## 13 | ## ----------------------------------------------------------------------- 14 | 15 | module Netconf 16 | class Transport 17 | attr_reader :rpc, :state, :session_id, :capabilities 18 | attr_writer :timeout, :waitio 19 | 20 | def initialize(&block) 21 | @state = :NETCONF_CLOSED 22 | @os_type = @args[:os_type] || Netconf::DEFAULT_OS_TYPE 23 | 24 | @rpc = Netconf::RPC::Executor.new(self, @os_type) 25 | @rpc_message_id = 1 26 | 27 | if block_given? 28 | open(&block = nil) # do not pass this block to open() 29 | yield self 30 | close 31 | end 32 | end # initialize 33 | 34 | def open? 35 | @state == :NETCONF_OPEN 36 | end 37 | 38 | def closed? 39 | @state == :NETCONF_CLOSED 40 | end 41 | 42 | def open(&block) # :yield: specialized transport open, generally not used 43 | raise Netconf::StateError if @state == :NETCONF_OPEN 44 | # block is used to deal with special open processing ... 45 | # this is *NOT* the block passed to initialize() 46 | raise Netconf::OpenError unless trans_open(&block) 47 | # read the from the server and parse out 48 | # the capabilities and session-id 49 | hello_rsp = Nokogiri::XML(trans_receive_hello) 50 | hello_rsp.remove_namespaces! 51 | 52 | @capabilities = hello_rsp.xpath('//capability').map(&:text) 53 | @session_id = hello_rsp.xpath('//session-id').text 54 | 55 | # send the 56 | trans_send_hello 57 | 58 | @state = :NETCONF_OPEN 59 | self 60 | end 61 | 62 | def trans_receive_hello 63 | trans_receive 64 | end 65 | 66 | def trans_send_hello 67 | trans_send( Netconf::RPC::MSG_HELLO ) 68 | trans_send( RPC::MSG_END ) 69 | end 70 | 71 | def has_capability?(capability) 72 | @capabilities.select { |c| c.include? capability }.pop 73 | # note: the caller could also simply use #grep on @capabilities 74 | end 75 | 76 | def close 77 | raise Netconf::StateError unless @state == :NETCONF_OPEN 78 | trans_close 79 | @state = :NETCONF_CLOSED 80 | self 81 | end 82 | 83 | # string in; string out 84 | def send_and_receive(cmd_str) 85 | trans_send(cmd_str) 86 | trans_send(RPC::MSG_END) 87 | trans_receive 88 | end 89 | 90 | def rpc_exec(cmd_nx) 91 | raise Netconf::StateError unless @state == :NETCONF_OPEN 92 | 93 | # add the mandatory message-id and namespace to the RPC 94 | 95 | rpc_nx = cmd_nx.parent.root 96 | rpc_nx.default_namespace = Netconf::NAMESPACE 97 | rpc_nx['message-id'] = @rpc_message_id.to_s 98 | @rpc_message_id += 1 99 | 100 | # send the XML command through the transport and 101 | # receive the response; then covert it to a Nokogiri XML 102 | # object so we can process it. 103 | 104 | rsp_nx = Nokogiri::XML(send_and_receive(cmd_nx.to_xml)) 105 | 106 | # the following removes only the default namespace (xmlns) 107 | # definitions from the document. This is an alternative 108 | # to using #remove_namespaces! which would remove everything 109 | # including vendor specific namespaces. So this approach is a 110 | # nice "compromise" ... just don't know what it does 111 | # performance-wise on large datasets. 112 | 113 | rsp_nx.traverse { |n| n.namespace = nil } 114 | 115 | # set the response context to the root node; 116 | 117 | rsp_nx = rsp_nx.root 118 | 119 | # check for rpc-error elements. these could be 120 | # located anywhere in the structured response 121 | 122 | rpc_errs = rsp_nx.xpath('//self::rpc-error') 123 | if rpc_errs.count.positive? 124 | 125 | # look for rpc-errors that have a severity == 'error' 126 | # in some cases the rpc-error is generated with 127 | # severity == 'warning' 128 | 129 | sev_err = rpc_errs.xpath('error-severity[. = "error"]') 130 | 131 | # if there are rpc-error with severity == 'error' 132 | # or if the caller wants to raise if severity == 'warning' 133 | # then generate the exception 134 | 135 | if sev_err.count.positive? || Netconf::raise_on_warning 136 | exception = Netconf::RPC.get_exception(cmd_nx) 137 | raise exception.new(self, cmd_nx, rsp_nx) 138 | end 139 | end 140 | 141 | # return the XML with context at toplevel element; i.e. 142 | # after the element 143 | # @@@/JLS: might this be ? isn't for Junos, but need to check 144 | # @@@/JLS: the generic case. 145 | 146 | rsp_nx.element_children 147 | end 148 | end # class: Transport 149 | end # module: Netconf 150 | -------------------------------------------------------------------------------- /lib/net/netconf/jnpr/rpc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ## ----------------------------------------------------------------------- 4 | ## This file contains the Junos specific RPC methods that are generated 5 | ## specifically and different as generated by the Netconf::RPC::Builder 6 | ## module. These are specifically the following: 7 | ## 8 | ## get_configuration - alternative NETCONF: 'get-config' 9 | ## load_configuration - alternative NETCONF: 'edit-config' 10 | ## lock_configuration - alternative NETCONF: 'lock' 11 | ## commit_configuration - alternative NETCONF: 'commit' 12 | ## 13 | ## note: unlock_configuration is not included in this file since 14 | ## the Netconf::RPC::Builder works "as-is" in this case 15 | ## ----------------------------------------------------------------------- 16 | 17 | module Netconf 18 | module RPC 19 | module Junos 20 | def lock_configuration 21 | lock('candidate') 22 | end 23 | 24 | def check_configuration 25 | validate('candidate') 26 | end 27 | 28 | def commit_configuration(params = nil, attrs = nil) 29 | rpc = Netconf::RPC::Builder.commit_configuration(params, attrs) 30 | Netconf::RPC.set_exception(rpc, Netconf::CommitError) 31 | @trans.rpc_exec(rpc) 32 | end 33 | 34 | def nokogiri_case(arg) 35 | filter = case arg 36 | when Nokogiri::XML::Builder then arg.doc.root 37 | when Nokogiri::XML::Document then arg.root 38 | else arg 39 | end 40 | end 41 | 42 | def get_configuration(*args) 43 | filter = nil 44 | 45 | while arg = args.shift 46 | case arg.class.to_s 47 | when /^Nokogiri/ 48 | nokogiri_case(arg) 49 | when 'Hash' then attrs = arg 50 | end 51 | end 52 | 53 | rpc = Nokogiri::XML('').root 54 | Netconf::RPC.add_attributes(rpc.first_element_child, attrs) if attrs 55 | 56 | if block_given? 57 | Nokogiri::XML::Builder.with(rpc.at('get-configuration')) do |xml| 58 | xml.configuration { yield(xml) } 59 | end 60 | elsif filter 61 | # filter must have toplevel = 62 | rpc.first_element_child << filter.dup 63 | end 64 | 65 | @trans.rpc_exec(rpc) 66 | end 67 | 68 | def load_configuration(*args) 69 | config = nil 70 | attrs = { format: 'xml' } 71 | 72 | while arg = args.shift 73 | case arg.class.to_s 74 | when /^Nokogiri/ 75 | nokogiri_case(arg) 76 | when 'Hash' then attrs.merge! arg 77 | when 'Array' then config = arg.join("\n") 78 | when 'String' then config = arg 79 | end 80 | end 81 | 82 | case attrs[:format] 83 | when 'set' 84 | toplevel = 'configuration-set' 85 | attrs[:format] = 'text' 86 | attrs[:action] = 'set' 87 | when 'text' 88 | toplevel = 'configuration-text' 89 | when 'xml' 90 | toplevel = 'configuration' 91 | end 92 | 93 | rpc = Nokogiri::XML('').root 94 | ld_cfg = rpc.first_element_child 95 | Netconf::RPC.add_attributes(ld_cfg, attrs) if attrs 96 | 97 | if block_given? 98 | if attrs[:format] == 'xml' 99 | Nokogiri::XML::Builder.with(ld_cfg) do |xml| 100 | xml.send(toplevel) { yield(xml) } 101 | end 102 | else 103 | config = yield # returns String | Array(of stringable) 104 | config = config.join("\n") if config.class == Array 105 | end 106 | end 107 | 108 | if config 109 | if attrs[:format] == 'xml' 110 | # config assumes toplevel = given 111 | ld_cfg << config.dup 112 | else 113 | # config is stringy, so just add it as the text node 114 | c_node = Nokogiri::XML::Node.new(toplevel, rpc) 115 | c_node.content = config 116 | ld_cfg << c_node 117 | end 118 | end 119 | 120 | # set a specific exception class on this RPC so it can be 121 | # properlly handled by the calling enviornment 122 | 123 | Netconf::RPC.set_exception(rpc, Netconf::EditError) 124 | @trans.rpc_exec(rpc) 125 | end # load_configuration 126 | 127 | def command(cmd_str, attrs = nil) 128 | rpc = Nokogiri::XML("#{cmd_str}").root 129 | Netconf::RPC.add_attributes(rpc.at('command'), attrs) if attrs 130 | @trans.rpc_exec(rpc) 131 | end 132 | 133 | ## contributed by 'dgjnpr' 134 | def request_pfe_execute(params = nil) 135 | raise ArgumentError, 'Manditorary argument :target missing' unless params[:target] 136 | raise ArgumentError, 'Manditorary argument :command missing' unless params[:command] 137 | 138 | rpc_nx = Nokogiri::XML::Builder.new do |xml| 139 | xml.rpc do 140 | xml.send('request-pfe-execute') do 141 | xml.send('target', params[:target]) 142 | if params[:command].class.to_s =~ /^Array/ 143 | params[:command].each { |cmd| xml.send('command', cmd) } 144 | elsif params[:command].class.to_s =~ /^String/ 145 | xml.send('command', params[:command]) 146 | end 147 | end 148 | end 149 | end 150 | @trans.rpc_exec(rpc_nx) 151 | end 152 | 153 | def rollback(n = 0) 154 | ArgumentError "rollback between 0 and 49 only" unless n.between?(0,49) 155 | reply = load_configuration(rollback: n) 156 | !reply.xpath('//ok').empty? # return true or false to indicate success or not 157 | end 158 | end # module: JUNOS 159 | end # module: RPC 160 | end # module: Netconf 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Netconf 2 | [![Gem Version](https://badge.fury.io/rb/netconf.svg)](https://badge.fury.io/rb/netconf) 3 | [![Dependency Status](https://gemnasium.com/badges/github.com/Juniper/net-netconf.svg)](https://gemnasium.com/github.com/Juniper/net-netconf) 4 | [![Build Status](https://travis-ci.org/Juniper/net-netconf.svg?branch=master)](https://travis-ci.org/Juniper/net-netconf) 5 | [![Code Climate](https://codeclimate.com/github/Juniper/net-netconf/badges/gpa.svg)](https://codeclimate.com/github/Juniper/net-netconf) 6 | [![Test Coverage](https://codeclimate.com/github/Juniper/net-netconf/badges/coverage.svg)](https://codeclimate.com/github/Juniper/net-netconf/coverage) 7 | 8 | ## Description 9 | Device management using the NETCONF protocol as specified in 10 | [RFC4741](http://tools.ietf.org/html/rfc4741) and 11 | [RFC6241](http://tools.ietf.org/html/rfc6241). 12 | 13 | ## Features 14 | * Extensible protocol transport framework for SSH and non-SSH 15 | * SSH transport using [Net::SSH](http://net-ssh.rubyforge.org) 16 | * Telnet transport using Net::Telnet (Ruby Library) 17 | * Serial transport using [Ruby/SerialPort](http://ruby-serialport.rubyforge.org/) 18 | 19 | * NETCONF Standard RPCs 20 | * get-config, edit-config 21 | * lock, unlock 22 | * validate, discard-changes 23 | 24 | * Flexible RPC mechanism 25 | * Netconf::RPC::Builder to metaprogram RPCs 26 | * Vendor extension framework for custom RPCs 27 | 28 | * XML processing using [Nokogiri](http://nokogiri.org) 29 | 30 | ## Synopsis 31 | 32 | ```ruby 33 | require 'net/netconf' 34 | 35 | # create the options hash for the new NETCONF session. If you are 36 | # using ssh-agent, then omit the :password 37 | 38 | login = { target: 'vsrx', username: 'root', password: 'Amnesiac' } 39 | 40 | # provide a block and the session will open, execute, and close 41 | 42 | Netconf::SSH.new( login ){ |dev| 43 | 44 | # perform the RPC command: 45 | # 46 | # 47 | # 48 | 49 | inv = dev.rpc.get_chassis_inventory 50 | 51 | # The response is in Nokogiri XML format for easy processing ... 52 | 53 | puts 'Chassis: ' + inv.xpath('chassis/description').text 54 | puts 'Chassis Serial-Number: ' + inv.xpath('chassis/serial-number').text 55 | } 56 | ``` 57 | 58 | Alternative explicity open, execute RPCs, and close 59 | 60 | ```ruby 61 | require 'net/netconf' 62 | 63 | login = { target: 'vsrx', username: 'root', password: 'Amnesiac' } 64 | 65 | dev = Netconf::SSH.new(login) 66 | dev.open 67 | 68 | inv = dev.rpc.get_chassis_inventory 69 | 70 | puts 'Chassis: ' + inv.xpath('chassis/description').text 71 | puts 'Chassis Serial-Number: ' + inv.xpath('chassis/serial-number').text 72 | 73 | dev.close 74 | ``` 75 | 76 | ## Using Netconf 77 | ### Remote Procedure Calls (RPCs) 78 | Each Netconf session provides a readable instance variable - __rpc__. This is used to execute Remote Procedure Calls (RPCs). The @rpc will include the NETCONF standard RPCs, any vendor specific extension, as well as the ability to metaprogram new onces via method_missing. 79 | 80 | Here are some examples to illustrate the metaprogamming: 81 | 82 | Without any parameters, the RPC is created by swapping underscores (_) to 83 | hyphens (-): 84 | 85 | ```ruby 86 | require 'net/netconf' 87 | 88 | dev.rpc.get_chassis_inventory 89 | 90 | # 91 | # 92 | # 93 | ``` 94 | 95 | You can optionally provide RPC parameters as a hash: 96 | 97 | ```ruby 98 | dev.rpc.get_interface_information(interface_name: 'ge-0/0/0', terse: true ) 99 | 100 | # 101 | # 102 | # ge-0/0/0 103 | # 104 | # 105 | # 106 | ``` 107 | 108 | You can additionally supply attributes that get assigned to the toplevel 109 | element. In this case You must enclose the parameters hash to disambiquate it 110 | from the attributes hash, or declare a variable for the parameters hash. 111 | 112 | ```ruby 113 | dev.rpc.get_interface_information({interface_name: 'ge-0/0/0', terse: true }, { format: 'text'}) 114 | 115 | # 116 | # 117 | # ge-0/0/0 118 | # 119 | # 120 | # 121 | ``` 122 | 123 | If you want to provide attributes, but no parameters, then: 124 | 125 | ```ruby 126 | dev.rpc.get_chassis_inventory(nil, format: 'text') 127 | 128 | # 129 | # 130 | # 131 | ``` 132 | 133 | ### Retrieving Configuration 134 | To retrieve configuration from a device, use the `get-config` RPC. Here is 135 | an example, but you can find others in the __examples__ directory: 136 | 137 | ```ruby 138 | require 'net/netconf' 139 | 140 | login = { target: 'vsrx', username: 'root', password: 'Amnesia' } 141 | 142 | puts "Connecting to device: #{login[:target]}" 143 | 144 | Netconf::SSH.new(login) do |dev| 145 | puts 'Connected.' 146 | 147 | # ---------------------------------------------------------------------- 148 | # retrieve the full config. Default source is 'running' 149 | # Alternatively you can pass the source name as a string parameter 150 | # to #get_config 151 | 152 | puts 'Retrieving full config, please wait ... ' 153 | cfgall = dev.rpc.get_config 154 | puts "Showing 'system' hierarchy ..." 155 | puts cfgall.xpath('configuration/system') # JUNOS toplevel config element is 156 | 157 | # ---------------------------------------------------------------------- 158 | # specifying a filter as a block to get_config 159 | 160 | cfgsvc1 = dev.rpc.get_config do |x| 161 | x.configuration { x.system { x.services } } 162 | end 163 | 164 | puts 'Retrieved services as BLOCK:' 165 | cfgsvc1.xpath('//services/*').each { |s| puts s.name } 166 | 167 | # ---------------------------------------------------------------------- 168 | # specifying a filter as a parameter to get_config 169 | 170 | filter = Nokogiri::XML::Builder.new do |x| 171 | x.configuration { x.system { x.services } } 172 | end 173 | 174 | cfgsvc2 = dev.rpc.get_config(filter) 175 | puts 'Retrieved services as PARAM:' 176 | cfgsvc2.xpath('//services/*').each { |s| puts s.name } 177 | 178 | cfgsvc3 = dev.rpc.get_config(filter) 179 | puts 'Retrieved services as PARAM, re-used filter' 180 | cfgsvc3.xpath('//services/*').each { |s| puts s.name } 181 | end 182 | ``` 183 | 184 | __NOTE__: There is a JUNOS RPC, `get-configuration`, that provides Juniper 185 | specific extensions as well. 186 | 187 | ### Changing Configuration 188 | To retrieve configuration from a device, use the `edit-config` RPC. Here is 189 | an example, but you can find others in the __examples__ directory: 190 | 191 | ```ruby 192 | require 'net/netconf' 193 | 194 | login = { target: 'vsrx', username: 'root', password: 'Amnesia' } 195 | 196 | new_host_name = 'vsrx-abc' 197 | 198 | puts "Connecting to device: #{login[:target]}" 199 | 200 | Netconf::SSH.new(login) do |dev| 201 | puts 'Connected!' 202 | 203 | target = 'candidate' 204 | 205 | # JUNOS toplevel element is 'configuration' 206 | 207 | location = Nokogiri::XML::Builder.new do |x| 208 | x.configuration { 209 | x.system { 210 | x.location { 211 | x.building 'Main Campus, A' 212 | x.floor 5 213 | x.rack 27 214 | } 215 | } 216 | } 217 | end 218 | 219 | begin 220 | rsp = dev.rpc.lock target 221 | 222 | # -------------------------------------------------------------------- 223 | # configuration as BLOCK 224 | 225 | rsp = dev.rpc.edit_config do |x| 226 | x.configuration { 227 | x.system { 228 | x.send(:'host-name', new_host_name ) 229 | } 230 | } 231 | end 232 | 233 | # -------------------------------------------------------------------- 234 | # configuration as PARAM 235 | 236 | rsp = dev.rpc.edit_config(location) 237 | 238 | rsp = dev.rpc.validate target 239 | rpc = dev.rpc.commit 240 | rpc = dev.rpc.unlock target 241 | 242 | rescue Netconf::LockError => e 243 | puts 'Lock error' 244 | rescue Netconf::EditError => e 245 | puts 'Edit error' 246 | rescue Netconf::ValidateError => e 247 | puts 'Validate error' 248 | rescue Netconf::CommitError => e 249 | puts 'Commit error' 250 | rescue Netconf::RpcError => e 251 | puts 'General RPC error' 252 | else 253 | puts 'Configuration Committed.' 254 | end 255 | end 256 | ``` 257 | 258 | __NOTE__: There is a JUNOS RPC, `load-configuration`, that provides 259 | Juniper specific extensions as well. 260 | 261 | ## Authors and Contributors 262 | * [Jeremy Schulman](www.linkedin.com/in/jeremyschulman), Juniper Networks 263 | * [Ankit Jain](http://www.linkedin.com/in/ankitj093), Juniper Networks 264 | * [Kevin Kirsche](mailto:Kev.Kirsche+GitHub@gmail.com) 265 | * [David Gethings](https://www.linkedin.com/in/david-gethings-59a2051/), Juniper Networks 266 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.1 3 | DisabledByDefault: true 4 | 5 | #################### Lint ################################ 6 | 7 | Lint/AmbiguousOperator: 8 | Description: >- 9 | Checks for ambiguous operators in the first argument of a 10 | method invocation without parentheses. 11 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' 12 | Enabled: true 13 | 14 | Lint/AmbiguousRegexpLiteral: 15 | Description: >- 16 | Checks for ambiguous regexp literals in the first argument of 17 | a method invocation without parenthesis. 18 | Enabled: true 19 | 20 | Lint/AssignmentInCondition: 21 | Description: "Don't use assignment in conditions." 22 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' 23 | Enabled: true 24 | 25 | Lint/BlockAlignment: 26 | Description: 'Align block ends correctly.' 27 | Enabled: true 28 | 29 | Lint/CircularArgumentReference: 30 | Description: "Don't refer to the keyword argument in the default value." 31 | Enabled: true 32 | 33 | Lint/ConditionPosition: 34 | Description: >- 35 | Checks for condition placed in a confusing position relative to 36 | the keyword. 37 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' 38 | Enabled: true 39 | 40 | Lint/Debugger: 41 | Description: 'Check for debugger calls.' 42 | Enabled: true 43 | 44 | Lint/DefEndAlignment: 45 | Description: 'Align ends corresponding to defs correctly.' 46 | Enabled: true 47 | 48 | Lint/DeprecatedClassMethods: 49 | Description: 'Check for deprecated class method calls.' 50 | Enabled: true 51 | 52 | Lint/DuplicateMethods: 53 | Description: 'Check for duplicate methods calls.' 54 | Enabled: true 55 | 56 | Lint/EachWithObjectArgument: 57 | Description: 'Check for immutable argument given to each_with_object.' 58 | Enabled: true 59 | 60 | Lint/ElseLayout: 61 | Description: 'Check for odd code arrangement in an else block.' 62 | Enabled: true 63 | 64 | Lint/EmptyEnsure: 65 | Description: 'Checks for empty ensure block.' 66 | Enabled: true 67 | 68 | Lint/EmptyInterpolation: 69 | Description: 'Checks for empty string interpolation.' 70 | Enabled: true 71 | 72 | Lint/EndAlignment: 73 | Description: 'Align ends correctly.' 74 | Enabled: true 75 | 76 | Lint/EndInMethod: 77 | Description: 'END blocks should not be placed inside method definitions.' 78 | Enabled: true 79 | 80 | Lint/EnsureReturn: 81 | Description: 'Do not use return in an ensure block.' 82 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' 83 | Enabled: true 84 | 85 | Lint/Eval: 86 | Description: 'The use of eval represents a serious security risk.' 87 | Enabled: true 88 | 89 | Lint/FormatParameterMismatch: 90 | Description: 'The number of parameters to format/sprint must match the fields.' 91 | Enabled: true 92 | 93 | Lint/HandleExceptions: 94 | Description: "Don't suppress exception." 95 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' 96 | Enabled: true 97 | 98 | Lint/InvalidCharacterLiteral: 99 | Description: >- 100 | Checks for invalid character literals with a non-escaped 101 | whitespace character. 102 | Enabled: true 103 | 104 | Lint/LiteralInCondition: 105 | Description: 'Checks of literals used in conditions.' 106 | Enabled: true 107 | 108 | Lint/LiteralInInterpolation: 109 | Description: 'Checks for literals used in interpolation.' 110 | Enabled: true 111 | 112 | Lint/Loop: 113 | Description: >- 114 | Use Kernel#loop with break rather than begin/end/until or 115 | begin/end/while for post-loop tests. 116 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' 117 | Enabled: true 118 | 119 | Lint/NestedMethodDefinition: 120 | Description: 'Do not use nested method definitions.' 121 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' 122 | Enabled: true 123 | 124 | Lint/NonLocalExitFromIterator: 125 | Description: 'Do not use return in iterator to cause non-local exit.' 126 | Enabled: true 127 | 128 | Lint/ParenthesesAsGroupedExpression: 129 | Description: >- 130 | Checks for method calls with a space before the opening 131 | parenthesis. 132 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 133 | Enabled: true 134 | 135 | Lint/RequireParentheses: 136 | Description: >- 137 | Use parentheses in the method call to avoid confusion 138 | about precedence. 139 | Enabled: true 140 | 141 | Lint/RescueException: 142 | Description: 'Avoid rescuing the Exception class.' 143 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' 144 | Enabled: true 145 | 146 | Lint/ShadowingOuterLocalVariable: 147 | Description: >- 148 | Do not use the same name as outer local variable 149 | for block arguments or block local variables. 150 | Enabled: true 151 | 152 | Lint/StringConversionInInterpolation: 153 | Description: 'Checks for Object#to_s usage in string interpolation.' 154 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' 155 | Enabled: true 156 | 157 | Lint/UnderscorePrefixedVariableName: 158 | Description: 'Do not use prefix `_` for a variable that is used.' 159 | Enabled: true 160 | 161 | Lint/UnneededDisable: 162 | Description: >- 163 | Checks for rubocop:disable comments that can be removed. 164 | Note: this cop is not disabled when disabling all cops. 165 | It must be explicitly disabled. 166 | Enabled: true 167 | 168 | Lint/UnusedBlockArgument: 169 | Description: 'Checks for unused block arguments.' 170 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 171 | Enabled: true 172 | 173 | Lint/UnusedMethodArgument: 174 | Description: 'Checks for unused method arguments.' 175 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 176 | Enabled: true 177 | 178 | Lint/UnreachableCode: 179 | Description: 'Unreachable code.' 180 | Enabled: true 181 | 182 | Lint/UselessAccessModifier: 183 | Description: 'Checks for useless access modifiers.' 184 | Enabled: true 185 | 186 | Lint/UselessAssignment: 187 | Description: 'Checks for useless assignment to a local variable.' 188 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 189 | Enabled: true 190 | 191 | Lint/UselessComparison: 192 | Description: 'Checks for comparison of something with itself.' 193 | Enabled: true 194 | 195 | Lint/UselessElseWithoutRescue: 196 | Description: 'Checks for useless `else` in `begin..end` without `rescue`.' 197 | Enabled: true 198 | 199 | Lint/UselessSetterCall: 200 | Description: 'Checks for useless setter call to a local variable.' 201 | Enabled: true 202 | 203 | Lint/Void: 204 | Description: 'Possible use of operator/literal/variable in void context.' 205 | Enabled: true 206 | 207 | ###################### Metrics #################################### 208 | 209 | Metrics/AbcSize: 210 | Description: >- 211 | A calculated magnitude based on number of assignments, 212 | branches, and conditions. 213 | Reference: 'http://c2.com/cgi/wiki?AbcMetric' 214 | Enabled: false 215 | Max: 20 216 | 217 | Metrics/BlockNesting: 218 | Description: 'Avoid excessive block nesting' 219 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' 220 | Enabled: true 221 | Max: 4 222 | 223 | Metrics/ClassLength: 224 | Description: 'Avoid classes longer than 250 lines of code.' 225 | Enabled: true 226 | Max: 250 227 | 228 | Metrics/CyclomaticComplexity: 229 | Description: >- 230 | A complexity metric that is strongly correlated to the number 231 | of test cases needed to validate a method. 232 | Enabled: true 233 | 234 | Metrics/LineLength: 235 | Description: 'Limit lines to 80 characters.' 236 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' 237 | Enabled: false 238 | 239 | Metrics/MethodLength: 240 | Description: 'Avoid methods longer than 30 lines of code.' 241 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' 242 | Enabled: true 243 | Max: 30 244 | 245 | Metrics/ModuleLength: 246 | Description: 'Avoid modules longer than 250 lines of code.' 247 | Enabled: true 248 | Max: 250 249 | 250 | Metrics/ParameterLists: 251 | Description: 'Avoid parameter lists longer than three or four parameters.' 252 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' 253 | Enabled: true 254 | 255 | Metrics/PerceivedComplexity: 256 | Description: >- 257 | A complexity metric geared towards measuring complexity for a 258 | human reader. 259 | Enabled: false 260 | 261 | ##################### Performance ############################# 262 | 263 | Performance/Count: 264 | Description: >- 265 | Use `count` instead of `select...size`, `reject...size`, 266 | `select...count`, `reject...count`, `select...length`, 267 | and `reject...length`. 268 | Enabled: true 269 | 270 | Performance/Detect: 271 | Description: >- 272 | Use `detect` instead of `select.first`, `find_all.first`, 273 | `select.last`, and `find_all.last`. 274 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' 275 | Enabled: true 276 | 277 | Performance/FlatMap: 278 | Description: >- 279 | Use `Enumerable#flat_map` 280 | instead of `Enumerable#map...Array#flatten(1)` 281 | or `Enumberable#collect..Array#flatten(1)` 282 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' 283 | Enabled: true 284 | EnabledForFlattenWithoutParams: false 285 | # If enabled, this cop will warn about usages of 286 | # `flatten` being called without any parameters. 287 | # This can be dangerous since `flat_map` will only flatten 1 level, and 288 | # `flatten` without any parameters can flatten multiple levels. 289 | 290 | Performance/ReverseEach: 291 | Description: 'Use `reverse_each` instead of `reverse.each`.' 292 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' 293 | Enabled: true 294 | 295 | Performance/Sample: 296 | Description: >- 297 | Use `sample` instead of `shuffle.first`, 298 | `shuffle.last`, and `shuffle[Fixnum]`. 299 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' 300 | Enabled: true 301 | 302 | Performance/Size: 303 | Description: >- 304 | Use `size` instead of `count` for counting 305 | the number of elements in `Array` and `Hash`. 306 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' 307 | Enabled: true 308 | 309 | Performance/StringReplacement: 310 | Description: >- 311 | Use `tr` instead of `gsub` when you are replacing the same 312 | number of characters. Use `delete` instead of `gsub` when 313 | you are deleting characters. 314 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' 315 | Enabled: true 316 | 317 | ##################### Rails ################################## 318 | 319 | Rails/ActionFilter: 320 | Description: 'Enforces consistent use of action filter methods.' 321 | Enabled: false 322 | 323 | Rails/Date: 324 | Description: >- 325 | Checks the correct usage of date aware methods, 326 | such as Date.today, Date.current etc. 327 | Enabled: false 328 | 329 | Rails/Delegate: 330 | Description: 'Prefer delegate method for delegations.' 331 | Enabled: false 332 | 333 | Rails/FindBy: 334 | Description: 'Prefer find_by over where.first.' 335 | Enabled: false 336 | 337 | Rails/FindEach: 338 | Description: 'Prefer all.find_each over all.find.' 339 | Enabled: false 340 | 341 | Rails/HasAndBelongsToMany: 342 | Description: 'Prefer has_many :through to has_and_belongs_to_many.' 343 | Enabled: false 344 | 345 | Rails/Output: 346 | Description: 'Checks for calls to puts, print, etc.' 347 | Enabled: false 348 | 349 | Rails/ReadWriteAttribute: 350 | Description: >- 351 | Checks for read_attribute(:attr) and 352 | write_attribute(:attr, val). 353 | Enabled: false 354 | 355 | Rails/ScopeArgs: 356 | Description: 'Checks the arguments of ActiveRecord scopes.' 357 | Enabled: false 358 | 359 | Rails/TimeZone: 360 | Description: 'Checks the correct usage of time zone aware methods.' 361 | StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' 362 | Reference: 'http://danilenko.org/2012/7/6/rails_timezones' 363 | Enabled: false 364 | 365 | Rails/Validation: 366 | Description: 'Use validates :attribute, hash of validations.' 367 | Enabled: false 368 | 369 | ################## Style ################################# 370 | 371 | Style/AccessModifierIndentation: 372 | Description: Check indentation of private/protected visibility modifiers. 373 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' 374 | Enabled: false 375 | 376 | Style/AccessorMethodName: 377 | Description: Check the naming of accessor methods for get_/set_. 378 | Enabled: false 379 | 380 | Style/Alias: 381 | Description: 'Use alias_method instead of alias.' 382 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' 383 | Enabled: false 384 | 385 | Style/AlignArray: 386 | Description: >- 387 | Align the elements of an array literal if they span more than 388 | one line. 389 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' 390 | Enabled: false 391 | 392 | Style/AlignHash: 393 | Description: >- 394 | Align the elements of a hash literal if they span more than 395 | one line. 396 | Enabled: false 397 | 398 | Style/AlignParameters: 399 | Description: >- 400 | Align the parameters of a method call if they span more 401 | than one line. 402 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' 403 | Enabled: false 404 | 405 | Style/AndOr: 406 | Description: 'Use &&/|| instead of and/or.' 407 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' 408 | Enabled: false 409 | 410 | Style/ArrayJoin: 411 | Description: 'Use Array#join instead of Array#*.' 412 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' 413 | Enabled: false 414 | 415 | Style/AsciiComments: 416 | Description: 'Use only ascii symbols in comments.' 417 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' 418 | Enabled: false 419 | 420 | Style/AsciiIdentifiers: 421 | Description: 'Use only ascii symbols in identifiers.' 422 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' 423 | Enabled: false 424 | 425 | Style/Attr: 426 | Description: 'Checks for uses of Module#attr.' 427 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' 428 | Enabled: false 429 | 430 | Style/BeginBlock: 431 | Description: 'Avoid the use of BEGIN blocks.' 432 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' 433 | Enabled: false 434 | 435 | Style/BarePercentLiterals: 436 | Description: 'Checks if usage of %() or %Q() matches configuration.' 437 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' 438 | Enabled: false 439 | 440 | Style/BlockComments: 441 | Description: 'Do not use block comments.' 442 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' 443 | Enabled: false 444 | 445 | Style/BlockEndNewline: 446 | Description: 'Put end statement of multiline block on its own line.' 447 | Enabled: false 448 | 449 | Style/BlockDelimiters: 450 | Description: >- 451 | Avoid using {...} for multi-line blocks (multiline chaining is 452 | always ugly). 453 | Prefer {...} over do...end for single-line blocks. 454 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 455 | Enabled: false 456 | 457 | Style/BracesAroundHashParameters: 458 | Description: 'Enforce braces style around hash parameters.' 459 | Enabled: false 460 | 461 | Style/CaseEquality: 462 | Description: 'Avoid explicit use of the case equality operator(===).' 463 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' 464 | Enabled: false 465 | 466 | Style/CaseIndentation: 467 | Description: 'Indentation of when in a case/when/[else/]end.' 468 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' 469 | Enabled: false 470 | 471 | Style/CharacterLiteral: 472 | Description: 'Checks for uses of character literals.' 473 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' 474 | Enabled: false 475 | 476 | Style/ClassAndModuleCamelCase: 477 | Description: 'Use CamelCase for classes and modules.' 478 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' 479 | Enabled: false 480 | 481 | Style/ClassAndModuleChildren: 482 | Description: 'Checks style of children classes and modules.' 483 | Enabled: false 484 | 485 | Style/ClassCheck: 486 | Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' 487 | Enabled: false 488 | 489 | Style/ClassMethods: 490 | Description: 'Use self when defining module/class methods.' 491 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods' 492 | Enabled: false 493 | 494 | Style/ClassVars: 495 | Description: 'Avoid the use of class variables.' 496 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' 497 | Enabled: false 498 | 499 | Style/ClosingParenthesisIndentation: 500 | Description: 'Checks the indentation of hanging closing parentheses.' 501 | Enabled: false 502 | 503 | Style/ColonMethodCall: 504 | Description: 'Do not use :: for method call.' 505 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' 506 | Enabled: false 507 | 508 | Style/CommandLiteral: 509 | Description: 'Use `` or %x around command literals.' 510 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' 511 | Enabled: false 512 | 513 | Style/CommentAnnotation: 514 | Description: 'Checks formatting of annotation comments.' 515 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' 516 | Enabled: false 517 | 518 | Style/CommentIndentation: 519 | Description: 'Indentation of comments.' 520 | Enabled: false 521 | 522 | Style/ConstantName: 523 | Description: 'Constants should use SCREAMING_SNAKE_CASE.' 524 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' 525 | Enabled: false 526 | 527 | Style/DefWithParentheses: 528 | Description: 'Use def with parentheses when there are arguments.' 529 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 530 | Enabled: false 531 | 532 | Style/PreferredHashMethods: 533 | Description: 'Checks for use of deprecated Hash methods.' 534 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' 535 | Enabled: false 536 | 537 | Style/Documentation: 538 | Description: 'Document classes and non-namespace modules.' 539 | Enabled: false 540 | 541 | Style/DotPosition: 542 | Description: 'Checks the position of the dot in multi-line method calls.' 543 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' 544 | Enabled: false 545 | 546 | Style/DoubleNegation: 547 | Description: 'Checks for uses of double negation (!!).' 548 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' 549 | Enabled: false 550 | 551 | Style/EachWithObject: 552 | Description: 'Prefer `each_with_object` over `inject` or `reduce`.' 553 | Enabled: false 554 | 555 | Style/ElseAlignment: 556 | Description: 'Align elses and elsifs correctly.' 557 | Enabled: false 558 | 559 | Style/EmptyElse: 560 | Description: 'Avoid empty else-clauses.' 561 | Enabled: false 562 | 563 | Style/EmptyLineBetweenDefs: 564 | Description: 'Use empty lines between defs.' 565 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' 566 | Enabled: false 567 | 568 | Style/EmptyLines: 569 | Description: "Don't use several empty lines in a row." 570 | Enabled: false 571 | 572 | Style/EmptyLinesAroundAccessModifier: 573 | Description: "Keep blank lines around access modifiers." 574 | Enabled: false 575 | 576 | Style/EmptyLinesAroundBlockBody: 577 | Description: "Keeps track of empty lines around block bodies." 578 | Enabled: false 579 | 580 | Style/EmptyLinesAroundClassBody: 581 | Description: "Keeps track of empty lines around class bodies." 582 | Enabled: false 583 | 584 | Style/EmptyLinesAroundModuleBody: 585 | Description: "Keeps track of empty lines around module bodies." 586 | Enabled: false 587 | 588 | Style/EmptyLinesAroundMethodBody: 589 | Description: "Keeps track of empty lines around method bodies." 590 | Enabled: false 591 | 592 | Style/EmptyLiteral: 593 | Description: 'Prefer literals to Array.new/Hash.new/String.new.' 594 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' 595 | Enabled: false 596 | 597 | Style/EndBlock: 598 | Description: 'Avoid the use of END blocks.' 599 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' 600 | Enabled: false 601 | 602 | Style/EndOfLine: 603 | Description: 'Use Unix-style line endings.' 604 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' 605 | Enabled: false 606 | 607 | Style/EvenOdd: 608 | Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' 609 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 610 | Enabled: false 611 | 612 | Style/ExtraSpacing: 613 | Description: 'Do not use unnecessary spacing.' 614 | Enabled: false 615 | 616 | Style/FileName: 617 | Description: 'Use snake_case for source file names.' 618 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' 619 | Enabled: false 620 | 621 | Style/InitialIndentation: 622 | Description: >- 623 | Checks the indentation of the first non-blank non-comment line in a file. 624 | Enabled: false 625 | 626 | Style/FirstParameterIndentation: 627 | Description: 'Checks the indentation of the first parameter in a method call.' 628 | Enabled: false 629 | 630 | Style/FlipFlop: 631 | Description: 'Checks for flip flops' 632 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' 633 | Enabled: false 634 | 635 | Style/For: 636 | Description: 'Checks use of for or each in multiline loops.' 637 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' 638 | Enabled: false 639 | 640 | Style/FormatString: 641 | Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' 642 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' 643 | Enabled: false 644 | 645 | Style/GlobalVars: 646 | Description: 'Do not introduce global variables.' 647 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' 648 | Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' 649 | Enabled: false 650 | 651 | Style/GuardClause: 652 | Description: 'Check for conditionals that can be replaced with guard clauses' 653 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 654 | Enabled: false 655 | 656 | Style/HashSyntax: 657 | Description: >- 658 | Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax 659 | { :a => 1, :b => 2 }. 660 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' 661 | Enabled: false 662 | 663 | Style/IfUnlessModifier: 664 | Description: >- 665 | Favor modifier if/unless usage when you have a 666 | single-line body. 667 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' 668 | Enabled: false 669 | 670 | Style/IfWithSemicolon: 671 | Description: 'Do not use if x; .... Use the ternary operator instead.' 672 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' 673 | Enabled: false 674 | 675 | Style/IndentationConsistency: 676 | Description: 'Keep indentation straight.' 677 | Enabled: false 678 | 679 | Style/IndentationWidth: 680 | Description: 'Use 2 spaces for indentation.' 681 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 682 | Enabled: false 683 | 684 | Style/IndentArray: 685 | Description: >- 686 | Checks the indentation of the first element in an array 687 | literal. 688 | Enabled: false 689 | 690 | Style/IndentHash: 691 | Description: 'Checks the indentation of the first key in a hash literal.' 692 | Enabled: false 693 | 694 | Style/InfiniteLoop: 695 | Description: 'Use Kernel#loop for infinite loops.' 696 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' 697 | Enabled: false 698 | 699 | Style/Lambda: 700 | Description: 'Use the new lambda literal syntax for single-line blocks.' 701 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' 702 | Enabled: false 703 | 704 | Style/LambdaCall: 705 | Description: 'Use lambda.call(...) instead of lambda.(...).' 706 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' 707 | Enabled: false 708 | 709 | Style/LeadingCommentSpace: 710 | Description: 'Comments should start with a space.' 711 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' 712 | Enabled: false 713 | 714 | Style/LineEndConcatenation: 715 | Description: >- 716 | Use \ instead of + or << to concatenate two string literals at 717 | line end. 718 | Enabled: false 719 | 720 | Style/MethodCallWithoutArgsParentheses: 721 | Description: 'Do not use parentheses for method calls with no arguments.' 722 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' 723 | Enabled: false 724 | 725 | Style/MethodDefParentheses: 726 | Description: >- 727 | Checks if the method definitions have or don't have 728 | parentheses. 729 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 730 | Enabled: false 731 | 732 | Style/MethodName: 733 | Description: 'Use the configured style when naming methods.' 734 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 735 | Enabled: false 736 | 737 | Style/ModuleFunction: 738 | Description: 'Checks for usage of `extend self` in modules.' 739 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' 740 | Enabled: false 741 | 742 | Style/MultilineBlockChain: 743 | Description: 'Avoid multi-line chains of blocks.' 744 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 745 | Enabled: false 746 | 747 | Style/MultilineBlockLayout: 748 | Description: 'Ensures newlines after multiline block do statements.' 749 | Enabled: false 750 | 751 | Style/MultilineIfThen: 752 | Description: 'Do not use then for multi-line if/unless.' 753 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' 754 | Enabled: false 755 | 756 | Style/MultilineOperationIndentation: 757 | Description: >- 758 | Checks indentation of binary operations that span more than 759 | one line. 760 | Enabled: false 761 | 762 | Style/MultilineTernaryOperator: 763 | Description: >- 764 | Avoid multi-line ?: (the ternary operator); 765 | use if/unless instead. 766 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' 767 | Enabled: false 768 | 769 | Style/NegatedIf: 770 | Description: >- 771 | Favor unless over if for negative conditions 772 | (or control flow or). 773 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' 774 | Enabled: false 775 | 776 | Style/NegatedWhile: 777 | Description: 'Favor until over while for negative conditions.' 778 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' 779 | Enabled: false 780 | 781 | Style/NestedTernaryOperator: 782 | Description: 'Use one expression per branch in a ternary operator.' 783 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' 784 | Enabled: false 785 | 786 | Style/Next: 787 | Description: 'Use `next` to skip iteration instead of a condition at the end.' 788 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 789 | Enabled: false 790 | 791 | Style/NilComparison: 792 | Description: 'Prefer x.nil? to x == nil.' 793 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 794 | Enabled: false 795 | 796 | Style/NonNilCheck: 797 | Description: 'Checks for redundant nil checks.' 798 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' 799 | Enabled: false 800 | 801 | Style/Not: 802 | Description: 'Use ! instead of not.' 803 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' 804 | Enabled: false 805 | 806 | Style/NumericLiterals: 807 | Description: >- 808 | Add underscores to large numeric literals to improve their 809 | readability. 810 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' 811 | Enabled: false 812 | 813 | Style/OneLineConditional: 814 | Description: >- 815 | Favor the ternary operator(?:) over 816 | if/then/else/end constructs. 817 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' 818 | Enabled: false 819 | 820 | Style/OpMethod: 821 | Description: 'When defining binary operators, name the argument other.' 822 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' 823 | Enabled: false 824 | 825 | Style/OptionalArguments: 826 | Description: >- 827 | Checks for optional arguments that do not appear at the end 828 | of the argument list 829 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments' 830 | Enabled: false 831 | 832 | Style/ParallelAssignment: 833 | Description: >- 834 | Check for simple usages of parallel assignment. 835 | It will only warn when the number of variables 836 | matches on both sides of the assignment. 837 | This also provides performance benefits 838 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' 839 | Enabled: false 840 | 841 | Style/ParenthesesAroundCondition: 842 | Description: >- 843 | Don't use parentheses around the condition of an 844 | if/unless/while. 845 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' 846 | Enabled: false 847 | 848 | Style/PercentLiteralDelimiters: 849 | Description: 'Use `%`-literal delimiters consistently' 850 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' 851 | Enabled: false 852 | 853 | Style/PercentQLiterals: 854 | Description: 'Checks if uses of %Q/%q match the configured preference.' 855 | Enabled: false 856 | 857 | Style/PerlBackrefs: 858 | Description: 'Avoid Perl-style regex back references.' 859 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' 860 | Enabled: false 861 | 862 | Style/PredicateName: 863 | Description: 'Check the names of predicate methods.' 864 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' 865 | Enabled: false 866 | 867 | Style/Proc: 868 | Description: 'Use proc instead of Proc.new.' 869 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' 870 | Enabled: false 871 | 872 | Style/RaiseArgs: 873 | Description: 'Checks the arguments passed to raise/fail.' 874 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' 875 | Enabled: false 876 | 877 | Style/RedundantBegin: 878 | Description: "Don't use begin blocks when they are not needed." 879 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' 880 | Enabled: false 881 | 882 | Style/RedundantException: 883 | Description: "Checks for an obsolete RuntimeException argument in raise/fail." 884 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' 885 | Enabled: false 886 | 887 | Style/RedundantReturn: 888 | Description: "Don't use return where it's not required." 889 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' 890 | Enabled: false 891 | 892 | Style/RedundantSelf: 893 | Description: "Don't use self where it's not needed." 894 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' 895 | Enabled: false 896 | 897 | Style/RegexpLiteral: 898 | Description: 'Use / or %r around regular expressions.' 899 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' 900 | Enabled: false 901 | 902 | Style/RescueEnsureAlignment: 903 | Description: 'Align rescues and ensures correctly.' 904 | Enabled: false 905 | 906 | Style/RescueModifier: 907 | Description: 'Avoid using rescue in its modifier form.' 908 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' 909 | Enabled: false 910 | 911 | Style/SelfAssignment: 912 | Description: >- 913 | Checks for places where self-assignment shorthand should have 914 | been used. 915 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' 916 | Enabled: false 917 | 918 | Style/Semicolon: 919 | Description: "Don't use semicolons to terminate expressions." 920 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' 921 | Enabled: false 922 | 923 | Style/SignalException: 924 | Description: 'Checks for proper usage of fail and raise.' 925 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' 926 | Enabled: false 927 | 928 | Style/SingleLineBlockParams: 929 | Description: 'Enforces the names of some block params.' 930 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' 931 | Enabled: false 932 | 933 | Style/SingleLineMethods: 934 | Description: 'Avoid single-line methods.' 935 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' 936 | Enabled: false 937 | 938 | Style/SpaceBeforeFirstArg: 939 | Description: >- 940 | Checks that exactly one space is used between a method name 941 | and the first argument for method calls without parentheses. 942 | Enabled: true 943 | 944 | Style/SpaceAfterColon: 945 | Description: 'Use spaces after colons.' 946 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 947 | Enabled: false 948 | 949 | Style/SpaceAfterComma: 950 | Description: 'Use spaces after commas.' 951 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 952 | Enabled: false 953 | 954 | Style/SpaceAroundKeyword: 955 | Description: 'Use spaces around keywords.' 956 | Enabled: false 957 | 958 | Style/SpaceAfterMethodName: 959 | Description: >- 960 | Do not put a space between a method name and the opening 961 | parenthesis in a method definition. 962 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 963 | Enabled: false 964 | 965 | Style/SpaceAfterNot: 966 | Description: Tracks redundant space after the ! operator. 967 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' 968 | Enabled: false 969 | 970 | Style/SpaceAfterSemicolon: 971 | Description: 'Use spaces after semicolons.' 972 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 973 | Enabled: false 974 | 975 | Style/SpaceBeforeBlockBraces: 976 | Description: >- 977 | Checks that the left block brace has or doesn't have space 978 | before it. 979 | Enabled: false 980 | 981 | Style/SpaceBeforeComma: 982 | Description: 'No spaces before commas.' 983 | Enabled: false 984 | 985 | Style/SpaceBeforeComment: 986 | Description: >- 987 | Checks for missing space between code and a comment on the 988 | same line. 989 | Enabled: false 990 | 991 | Style/SpaceBeforeSemicolon: 992 | Description: 'No spaces before semicolons.' 993 | Enabled: false 994 | 995 | Style/SpaceInsideBlockBraces: 996 | Description: >- 997 | Checks that block braces have or don't have surrounding space. 998 | For blocks taking parameters, checks that the left brace has 999 | or doesn't have trailing space. 1000 | Enabled: false 1001 | 1002 | Style/SpaceAroundBlockParameters: 1003 | Description: 'Checks the spacing inside and after block parameters pipes.' 1004 | Enabled: false 1005 | 1006 | Style/SpaceAroundEqualsInParameterDefault: 1007 | Description: >- 1008 | Checks that the equals signs in parameter default assignments 1009 | have or don't have surrounding space depending on 1010 | configuration. 1011 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' 1012 | Enabled: false 1013 | 1014 | Style/SpaceAroundOperators: 1015 | Description: 'Use a single space around operators.' 1016 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 1017 | Enabled: false 1018 | 1019 | Style/SpaceInsideBrackets: 1020 | Description: 'No spaces after [ or before ].' 1021 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 1022 | Enabled: false 1023 | 1024 | Style/SpaceInsideHashLiteralBraces: 1025 | Description: "Use spaces inside hash literal braces - or don't." 1026 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 1027 | Enabled: false 1028 | 1029 | Style/SpaceInsideParens: 1030 | Description: 'No spaces after ( or before ).' 1031 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 1032 | Enabled: false 1033 | 1034 | Style/SpaceInsideRangeLiteral: 1035 | Description: 'No spaces inside range literals.' 1036 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' 1037 | Enabled: false 1038 | 1039 | Style/SpaceInsideStringInterpolation: 1040 | Description: 'Checks for padding/surrounding spaces inside string interpolation.' 1041 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation' 1042 | Enabled: false 1043 | 1044 | Style/SpecialGlobalVars: 1045 | Description: 'Avoid Perl-style global variables.' 1046 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' 1047 | Enabled: false 1048 | 1049 | Style/StringLiterals: 1050 | Description: 'Checks if uses of quotes match the configured preference.' 1051 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' 1052 | Enabled: false 1053 | 1054 | Style/StringLiteralsInInterpolation: 1055 | Description: >- 1056 | Checks if uses of quotes inside expressions in interpolated 1057 | strings match the configured preference. 1058 | Enabled: false 1059 | 1060 | Style/StructInheritance: 1061 | Description: 'Checks for inheritance from Struct.new.' 1062 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new' 1063 | Enabled: false 1064 | 1065 | Style/SymbolLiteral: 1066 | Description: 'Use plain symbols instead of string symbols when possible.' 1067 | Enabled: false 1068 | 1069 | Style/SymbolProc: 1070 | Description: 'Use symbols as procs instead of blocks when possible.' 1071 | Enabled: false 1072 | 1073 | Style/Tab: 1074 | Description: 'No hard tabs.' 1075 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 1076 | Enabled: false 1077 | 1078 | Style/TrailingBlankLines: 1079 | Description: 'Checks trailing blank lines and final newline.' 1080 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' 1081 | Enabled: false 1082 | 1083 | Style/TrailingCommaInArguments: 1084 | Description: 'Checks for trailing comma in parameter lists.' 1085 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma' 1086 | Enabled: false 1087 | 1088 | Style/TrailingCommaInLiteral: 1089 | Description: 'Checks for trailing comma in literals.' 1090 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' 1091 | Enabled: false 1092 | 1093 | Style/TrailingWhitespace: 1094 | Description: 'Avoid trailing whitespace.' 1095 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' 1096 | Enabled: false 1097 | 1098 | Style/TrivialAccessors: 1099 | Description: 'Prefer attr_* methods to trivial readers/writers.' 1100 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' 1101 | Enabled: false 1102 | 1103 | Style/UnlessElse: 1104 | Description: >- 1105 | Do not use unless with else. Rewrite these with the positive 1106 | case first. 1107 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' 1108 | Enabled: false 1109 | 1110 | Style/UnneededCapitalW: 1111 | Description: 'Checks for %W when interpolation is not needed.' 1112 | Enabled: false 1113 | 1114 | Style/UnneededPercentQ: 1115 | Description: 'Checks for %q/%Q when single quotes or double quotes would do.' 1116 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' 1117 | Enabled: false 1118 | 1119 | Style/TrailingUnderscoreVariable: 1120 | Description: >- 1121 | Checks for the usage of unneeded trailing underscores at the 1122 | end of parallel variable assignment. 1123 | Enabled: false 1124 | 1125 | Style/VariableInterpolation: 1126 | Description: >- 1127 | Don't interpolate global, instance and class variables 1128 | directly in strings. 1129 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' 1130 | Enabled: false 1131 | 1132 | Style/VariableName: 1133 | Description: 'Use the configured style when naming variables.' 1134 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 1135 | Enabled: false 1136 | 1137 | Style/WhenThen: 1138 | Description: 'Use when x then ... for one-line cases.' 1139 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' 1140 | Enabled: false 1141 | 1142 | Style/WhileUntilDo: 1143 | Description: 'Checks for redundant do after while or until.' 1144 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' 1145 | Enabled: false 1146 | 1147 | Style/WhileUntilModifier: 1148 | Description: >- 1149 | Favor modifier while/until usage when you have a 1150 | single-line body. 1151 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' 1152 | Enabled: false 1153 | 1154 | Style/WordArray: 1155 | Description: 'Use %w or %W for arrays of words.' 1156 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' 1157 | Enabled: false --------------------------------------------------------------------------------