rtprov 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a398e411bb4dad6a5a9d9729fc8c9c7c6c88620836d08eb4edfef767943eea82
4
+ data.tar.gz: 6e475a17877c5bab3b6dff77a347582e0ac5d727684584ace3d9b0ad25e5e517
5
+ SHA512:
6
+ metadata.gz: 45e51ed23305e428916884a2c8dd0512332f1deed16edb25de19ba06e50e62e4be8c25e8e1a21929b84049df6ff6e43e41c221607d5cedc6f7046bef412024bc
7
+ data.tar.gz: b1580e8ea6d1580a544c405bbac4f150813492bade5f2f84ca7763a5ec86e1aec0a115651c5346889b78930010ddfdb0883e6fd3f154cd7042205b1abf28dfcd
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /routers/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,126 @@
1
+ AllCops:
2
+ Exclude:
3
+ - bin/*
4
+ - node_modules/**/*
5
+
6
+ Layout/CaseIndentation:
7
+ EnforcedStyle: end
8
+
9
+ Layout/EndAlignment:
10
+ EnforcedStyleAlignWith: variable
11
+
12
+ Layout/EmptyLinesAroundAccessModifier:
13
+ EnforcedStyle: only_before
14
+
15
+ Layout/SpaceInLambdaLiteral:
16
+ EnforcedStyle: require_space
17
+
18
+ Layout/SpaceInsideBlockBraces:
19
+ SpaceBeforeBlockParameters: false
20
+
21
+ Layout/SpaceInsideHashLiteralBraces:
22
+ EnforcedStyle: no_space
23
+
24
+ Metrics/AbcSize:
25
+ Enabled: false
26
+
27
+ Metrics/BlockLength:
28
+ Enabled: false
29
+
30
+ Metrics/ClassLength:
31
+ Enabled: false
32
+
33
+ Metrics/CyclomaticComplexity:
34
+ Enabled: false
35
+
36
+ Metrics/LineLength:
37
+ Enabled: false
38
+
39
+ Metrics/MethodLength:
40
+ Enabled: false
41
+
42
+ Metrics/ModuleLength:
43
+ Enabled: false
44
+
45
+ Metrics/PerceivedComplexity:
46
+ Enabled: false
47
+
48
+ Naming/HeredocDelimiterNaming:
49
+ Enabled: false
50
+
51
+ Naming/PredicateName:
52
+ Enabled: false
53
+
54
+ Naming/UncommunicativeMethodParamName:
55
+ Enabled: false
56
+
57
+ Style/Alias:
58
+ EnforcedStyle: prefer_alias_method
59
+
60
+ Style/AsciiComments:
61
+ Enabled: false
62
+
63
+ Style/BlockDelimiters:
64
+ Enabled: false
65
+
66
+ Style/Documentation:
67
+ Enabled: false
68
+
69
+ Style/EmptyCaseCondition:
70
+ Enabled: false
71
+
72
+ Style/EmptyMethod:
73
+ EnforcedStyle: expanded
74
+
75
+ Style/FrozenStringLiteralComment:
76
+ Enabled: false
77
+
78
+ Style/HashSyntax:
79
+ Exclude:
80
+ - Rakefile
81
+ - "**/*.rake"
82
+
83
+ Style/Lambda:
84
+ EnforcedStyle: literal
85
+
86
+ Style/IfUnlessModifier:
87
+ Enabled: false
88
+
89
+ Style/MultilineBlockChain:
90
+ Enabled: false
91
+
92
+ Style/NumericLiterals:
93
+ Enabled: false
94
+
95
+ Style/NumericPredicate:
96
+ Enabled: false
97
+
98
+ Style/PercentLiteralDelimiters:
99
+ PreferredDelimiters:
100
+ '%i': '()'
101
+ '%w': '()'
102
+ '%r': '()'
103
+
104
+ Style/SpecialGlobalVars:
105
+ Enabled: false
106
+
107
+ Style/StringLiterals:
108
+ EnforcedStyle: double_quotes
109
+
110
+ Style/StringLiteralsInInterpolation:
111
+ EnforcedStyle: double_quotes
112
+
113
+ Style/SymbolArray:
114
+ Enabled: false
115
+
116
+ Style/TrailingCommaInArguments:
117
+ EnforcedStyleForMultiline: consistent_comma
118
+
119
+ Style/TrailingCommaInArrayLiteral:
120
+ EnforcedStyleForMultiline: consistent_comma
121
+
122
+ Style/TrailingCommaInHashLiteral:
123
+ EnforcedStyleForMultiline: consistent_comma
124
+
125
+ Style/ZeroLengthPredicate:
126
+ Enabled: false
@@ -0,0 +1,3 @@
1
+ # v0.1.0
2
+
3
+ * initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rtprov.gemspec
4
+ gemspec
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rtprov (0.1.0)
5
+ reversible_cryptography
6
+ thor
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ast (2.4.0)
12
+ jaro_winkler (1.5.3)
13
+ parallel (1.17.0)
14
+ parser (2.6.3.0)
15
+ ast (~> 2.4.0)
16
+ rainbow (3.0.0)
17
+ rake (10.5.0)
18
+ reversible_cryptography (0.5.0)
19
+ thor
20
+ rubocop (0.74.0)
21
+ jaro_winkler (~> 1.5.1)
22
+ parallel (~> 1.10)
23
+ parser (>= 2.6)
24
+ rainbow (>= 2.2.2, < 4.0)
25
+ ruby-progressbar (~> 1.7)
26
+ unicode-display_width (>= 1.4.0, < 1.7)
27
+ ruby-progressbar (1.10.1)
28
+ thor (0.20.3)
29
+ unicode-display_width (1.6.0)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bundler (~> 2.0)
36
+ rake (~> 10.0)
37
+ rtprov!
38
+ rubocop (~> 0.74.0)
39
+
40
+ BUNDLED WITH
41
+ 2.0.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 labocho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ # rtprov
2
+
3
+ Yamaha router (RTX or NVR series etc.) provisioning tool.
4
+
5
+ ## Installation
6
+
7
+ $ gem install rtprov
8
+
9
+ ## Requirements
10
+
11
+ rtprov requires following commands. Please install these.
12
+
13
+ * colordiff (or diff)
14
+ * lftp
15
+ * ssh
16
+ * which
17
+
18
+ ## Preparation
19
+
20
+ * Enable SSH and SFTP access.
21
+ * Create user and allow SSH access and administration.
22
+
23
+ And you must know following passwords.
24
+
25
+ * User password
26
+ * Administrator password
27
+ * Anonymous user password
28
+
29
+ ## Usage
30
+
31
+ ### rtprov new
32
+
33
+ `rtprov new` creates directory and some files.
34
+ Please run other commands in created directory.
35
+
36
+ It creates `./encryption_key` to encrypt router configurations.
37
+ .gitignore ignores this. Please store it securely.
38
+
39
+ $ rtprov new my_office
40
+
41
+
42
+ ### rtprov edit
43
+
44
+ `rtprov edit` creates or edits router configuration file in `./routers` directory. It is encrypted by `./encryption_key` file.
45
+ rtprov launches `ENV["RTPROV_EDITOR"]` or `ENV["EDITOR"]` to edit file.
46
+
47
+ # Launch editor and create/update routers/my_router.yml.enc
48
+ $ rtprov edit my_router
49
+
50
+
51
+ ### rtprov show
52
+
53
+ `rtprov show` print router configuration to stdout.
54
+
55
+ # Prints decrypted routers/my_router.yml.enc
56
+ $ rtprov show my_router
57
+
58
+ ### rtprov get
59
+
60
+ `rtprov get` gets config file from router and print to stdout.
61
+ If `-n, --number` option is not specified, it gets `config0`.
62
+
63
+ # Get config0 from my_router and print to stdout
64
+ $ rtprov get my_router
65
+ # Get config1 from my_router and print to stdout
66
+ $ rtprov get --number 1 my_router
67
+
68
+
69
+ ### rtprov put
70
+
71
+ `rtprov put` puts config file to router and load it.
72
+ Second argument is template name. If you create `templates/my_config.erb`, template name is `my_config`.
73
+ If `-n, --number` option is not specified, it gets `config0`.
74
+
75
+ It prints diff before transfer and ask you.
76
+ rtprov uses `ENV["RTPROV_DIFF"]` or `colordiff` or `diff` to print diff.
77
+
78
+ # Rendering templates/my_config.erb and put to my_route as config0 and load it.
79
+ $ rtprov put my_router my_config
80
+ # Rendering templates/my_config.erb and put to my_route as config1 and load it.
81
+ $ rtprov put my_router --number 1 my_config
82
+
83
+
84
+ ## Development
85
+
86
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
87
+
88
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
89
+
90
+ ## Contributing
91
+
92
+ Bug reports and pull requests are welcome on GitHub at https://github.com/labocho/rtprov.
93
+
94
+ ## License
95
+
96
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rtprov"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require "rtprov"
3
+
4
+ Rtprov::CLI.start(ARGV)
@@ -0,0 +1,13 @@
1
+ require "rtprov/version"
2
+
3
+ module Rtprov
4
+ class Error < StandardError; end
5
+
6
+ require_relative "rtprov/cli"
7
+ require_relative "rtprov/encryption"
8
+ require_relative "rtprov/initializer"
9
+ require_relative "rtprov/router"
10
+ require_relative "rtprov/session"
11
+ require_relative "rtprov/sftp"
12
+ require_relative "rtprov/template"
13
+ end
@@ -0,0 +1,83 @@
1
+ require "thor"
2
+ require "yaml"
3
+
4
+ module Rtprov
5
+ class CLI < ::Thor
6
+ desc "new REPO", "Create new rtprov repository"
7
+ def new(name)
8
+ Initializer.run(name)
9
+ end
10
+
11
+ desc "edit ROUTER", "Edit router config"
12
+ def edit(router_name)
13
+ Router.edit(router_name)
14
+ end
15
+
16
+ desc "show ROUTER", "Show router config"
17
+ def show(router_name)
18
+ puts Router.decrypt(router_name)
19
+ end
20
+
21
+ desc "get ROUTER", "Get config from router"
22
+ option :number, type: :numeric, default: 0, aliases: :n, desc: "Configuration number"
23
+ def get(router_name)
24
+ router = Router.load(router_name)
25
+ sftp = Sftp.new(router.host, router.user, router.administrator_password)
26
+ puts sftp.get("/system/config#{options[:number]}")
27
+ end
28
+
29
+ desc "put ROUTER TEMPLATE", "Put config from router"
30
+ option :number, type: :numeric, default: 0, aliases: :n, desc: "Configuration number"
31
+ option :force, type: :boolean, default: false, aliases: :f, desc: "Don't ask to trasfer and load config"
32
+ def put(router_name, template_name)
33
+ current_file = "/system/config#{options[:number]}"
34
+ router = Router.load(router_name)
35
+
36
+ template = Template.find(router_name, template_name)
37
+ new_config = template.render(router.variables)
38
+
39
+ sftp = Sftp.new(router.host, router.user, router.administrator_password)
40
+ current_config = sftp.get(current_file)
41
+ diff = ENV["RTPROV_DIFF"] || %w(colordiff diff).find {|cmd| system("which", cmd, out: "/dev/null", err: "/dev/null") }
42
+
43
+ Dir.mktmpdir do |dir|
44
+ Dir.chdir(dir) do
45
+ File.write("new.conf", new_config.gsub(/^#.*$/, "").gsub(/(\r\n|\r|\n)+/, "\r\n"))
46
+ File.write("current.conf", current_config.gsub(/^#.*$/, "").gsub(/(\r\n|\r|\n)+/, "\r\n"))
47
+ system("#{diff} -u current.conf new.conf", out: $stdout, err: $stderr)
48
+
49
+ unless options[:force]
50
+ loop do
51
+ print "apply? (y/n): "
52
+ case $stdin.gets.strip
53
+ when "y"
54
+ break
55
+ when "n"
56
+ return nil
57
+ end
58
+ end
59
+ end
60
+
61
+ sftp.put("new.conf", current_file)
62
+ Session.start(router) do |s|
63
+ s.exec_with_passwords "load config #{options[:number]} silent no-key-generate"
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ desc "ls", "List routers"
70
+ def ls
71
+ Router.names.each do |name|
72
+ puts name
73
+ end
74
+ end
75
+
76
+ desc "ssh ROUTER", "exec ssh to router"
77
+ def ssh(router_name)
78
+ router = Router.load(router_name)
79
+ warn "Password: #{router.password}"
80
+ exec "ssh", "#{router.user}@#{router.host}"
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,38 @@
1
+ require "reversible_cryptography"
2
+ require "securerandom"
3
+
4
+ module Rtprov
5
+ class Encryption
6
+ KEY_FILE = "encryption_key".freeze
7
+
8
+ attr_reader :key
9
+
10
+ def self.load_key
11
+ ENV["ENCRYPTION_KEY"] || (File.exist?(KEY_FILE) && File.read(KEY_FILE).strip) || raise("ENCRYPTION_KEY env or encryption_key file not found")
12
+ end
13
+
14
+ def self.encrypt(plain, key = load_key)
15
+ new(key).encrypt(plain)
16
+ end
17
+
18
+ def self.decrypt(encrypted, key = load_key)
19
+ new(key).decrypt(encrypted)
20
+ end
21
+
22
+ def self.generate_key
23
+ SecureRandom.base64(512)
24
+ end
25
+
26
+ def initialize(key)
27
+ @key = key.dup.freeze
28
+ end
29
+
30
+ def encrypt(plain)
31
+ ReversibleCryptography::Message.encrypt(plain, key)
32
+ end
33
+
34
+ def decrypt(encrypted)
35
+ ReversibleCryptography::Message.decrypt(encrypted, key)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,76 @@
1
+ require "fileutils"
2
+ require "open3"
3
+ require "shellwords"
4
+
5
+ module Rtprov
6
+ class Initializer
7
+ include FileUtils::Verbose
8
+
9
+ attr_reader :name
10
+
11
+ def self.run(name)
12
+ new(name).run
13
+ end
14
+
15
+ def initialize(name)
16
+ @name = name.dup.freeze
17
+ end
18
+
19
+ def run
20
+ raise "Already exists #{name} directory" if File.exist?(name)
21
+
22
+ mkdir name
23
+ Dir.chdir(name) do
24
+ mkdir "routers"
25
+ touch "routers/.keep"
26
+
27
+ mkdir "templates"
28
+ touch "templates/.keep"
29
+
30
+ puts "Create encryption_key"
31
+ key = Encryption.generate_key
32
+ File.write("encryption_key", key)
33
+
34
+ exec "bundle init"
35
+ exec "bundle add rtprov -v #{VERSION}"
36
+ exec "bundle binstubs rtprov"
37
+
38
+ puts "Create .gitignore"
39
+ gitignore = <<~EOS
40
+ /encryption_key
41
+ EOS
42
+ File.write(".gitignore", gitignore)
43
+
44
+ exec "git init"
45
+ exec "git add ."
46
+ end
47
+
48
+ puts <<~EOS
49
+
50
+ ============================================================
51
+ !!! Please remember `encryption_key`. Git ignores it. !!!
52
+ ============================================================
53
+
54
+ And do below.
55
+ 1. cd #{name}
56
+ 2. bin/rtprov edit YOUR_ROUTER_NAME
57
+ 3. bin/rtprov get YOUR_ROUTER_NAME > tempaltes/config.erb
58
+ 4. Extract credentials in templates/config.erb
59
+ EOS
60
+ end
61
+
62
+ private
63
+ def exec(cmd, *args)
64
+ puts "#{cmd} #{args.shelljoin}"
65
+
66
+ o, e, s = Open3.capture3(cmd, *args)
67
+
68
+ unless s.success?
69
+ warn e
70
+ raise "`#{cmd} #{args.shelljoin}` failed"
71
+ end
72
+
73
+ o
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,76 @@
1
+ require "tmpdir"
2
+ require "shellwords"
3
+
4
+ module Rtprov
5
+ class Router
6
+ ATTRIBUTES = %w(host user password administrator_password anonymous_password variables).map(&:freeze).freeze
7
+
8
+ attr_reader :name, *ATTRIBUTES
9
+
10
+ def self.edit(name)
11
+ encrypted_file = "routers/#{name}.yml.enc"
12
+ decrypted = if File.exist?(encrypted_file)
13
+ Encryption.decrypt(File.read(encrypted_file))
14
+ else
15
+ <<~YAML
16
+ host: 127.0.0.1
17
+ user: admin
18
+ password: opensesame
19
+ administrator_password: opensesame
20
+ anonymous_password: anonymous_password
21
+ variables: {}
22
+ YAML
23
+ end
24
+
25
+ Dir.mktmpdir do |dir|
26
+ temp = "#{dir}/#{name}.yml"
27
+ File.write(temp, decrypted)
28
+
29
+ if system("#{editor} #{temp.shellescape}", out: $stdout, err: $stderr)
30
+ encrypted = Encryption.encrypt(File.read(temp))
31
+ File.write(encrypted_file, encrypted)
32
+ warn "Saved to #{encrypted_file}"
33
+ else
34
+ warn "Not saved"
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.editor
40
+ return ENV["RTPROV_EDITOR"] if ENV["RTPROV_EDITOR"]
41
+
42
+ # rubocop: disable Lint/HandleExceptions
43
+ begin
44
+ o, _e, s = Open3.capture3("git config core.editor")
45
+ return o.strip if s.success?
46
+ rescue Errno::ENOENT
47
+ end
48
+ # rubocop: enable Lint/HandleExceptions
49
+
50
+ ENV["EDITOR"]
51
+ end
52
+
53
+ def self.decrypt(name)
54
+ Encryption.decrypt(File.read("routers/#{name}.yml.enc"))
55
+ end
56
+
57
+ def self.names
58
+ Dir["routers/*.yml.enc"].map {|path|
59
+ File.basename(path).gsub(/\.yml\.enc\z/, "")
60
+ }.sort
61
+ end
62
+
63
+ def self.load(name)
64
+ new(name, YAML.safe_load(decrypt(name)))
65
+ end
66
+
67
+ def initialize(name, attributes)
68
+ @name = name
69
+
70
+ attributes.each do |k, v|
71
+ ATTRIBUTES.include?(k) || raise("Unknown attribute found `#{k}`")
72
+ instance_variable_set "@#{k}", v
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,93 @@
1
+ require "expect"
2
+ require "pty"
3
+ require "shellwords"
4
+
5
+ module Rtprov
6
+ class Session
7
+ attr_reader :router, :reader, :writer, :prompt, :prompt_pattern
8
+
9
+ def self.start(router, &block)
10
+ cmd = [
11
+ "ssh",
12
+ "#{router.user}@#{router.host}",
13
+ ].shelljoin
14
+
15
+ PTY.getpty(cmd) do |r, w, _pid|
16
+ w.sync = true
17
+
18
+ r.expect(/password/)
19
+ w.puts router.password
20
+ prompt = r.expect(/^(.*>) /)[1]
21
+
22
+ session = new(router, r, w, prompt)
23
+ session.exec("console character en.ascii")
24
+ session.exec("console lines infinity") # disable pager
25
+ session.exec("console columns 200")
26
+
27
+ session.as_administrator(&block)
28
+
29
+ w.puts "exit"
30
+ end
31
+ end
32
+
33
+ def initialize(router, reader, writer, prompt = ">")
34
+ @router = router
35
+ @reader = reader
36
+ @writer = writer
37
+ @prompt = prompt.dup.freeze
38
+ @prompt_pattern = Regexp.compile("^" + Regexp.escape(prompt) + " ").freeze
39
+ end
40
+
41
+ def exec(cmd)
42
+ writer.puts cmd
43
+ out, * = reader.expect(prompt_pattern)
44
+
45
+ unless out
46
+ raise "Command `#{cmd}` timed out"
47
+ end
48
+
49
+ out.each_line.to_a[1..-2].join # remove first line like '> cmd' and last line line '> '
50
+ end
51
+
52
+ def exec_with_passwords(cmd)
53
+ writer.puts cmd
54
+
55
+ reader.expect(/^Login Password: /)
56
+ writer.puts router.anonymous_password
57
+
58
+ reader.expect(/^Administrator Password: /)
59
+ writer.puts router.administrator_password
60
+
61
+ writer.puts "console prompt '#{prompt.gsub(/\#$/, "")}'" # load config may change prompt
62
+ out, * = reader.expect(prompt_pattern)
63
+
64
+ unless out
65
+ raise "Command `#{cmd}` timed out"
66
+ end
67
+
68
+ out.each_line.to_a[1..-2].join # remove first line like '> cmd' and last line line '> '
69
+ end
70
+
71
+ def as_administrator(&block)
72
+ writer.puts "administrator"
73
+ reader.expect(/Password: /)
74
+ writer.puts router.administrator_password
75
+ reader.expect(/^.*# /)
76
+
77
+ begin
78
+ # set new prompt because default administrator prompt "# " matches config file comment etc.
79
+ session = self.class.new(router, reader, writer, "RTPROV#")
80
+ session.exec "console prompt RTPROV"
81
+ block.call(session)
82
+ ensure
83
+ writer.puts "console prompt '#{prompt.gsub(/>$/, "")}'"
84
+ reader.expect(/^.*# /)
85
+ end
86
+
87
+ writer.puts "exit"
88
+ reader.expect "Save new configuration ? (Y/N)"
89
+ writer.puts "Y"
90
+ reader.expect(prompt_pattern)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,37 @@
1
+ require "open3"
2
+ require "tmpdir"
3
+
4
+ module Rtprov
5
+ class Sftp
6
+ attr_reader :host, :user, :password
7
+
8
+ def initialize(host, user, password)
9
+ @host = host.dup.freeze
10
+ @user = user.dup.freeze
11
+ @password = password.dup.freeze
12
+ end
13
+
14
+ def get(src)
15
+ Dir.mktmpdir do |dir|
16
+ dest = File.join(dir, File.basename(src))
17
+ run "get #{src} -o #{dest}"
18
+ File.read(dest)
19
+ end
20
+ end
21
+
22
+ def put(src, dest)
23
+ run "put #{src} -o #{dest}"
24
+ end
25
+
26
+ private
27
+ def run(command)
28
+ # use lftp (instead of sftp) to specify user and password by arguments
29
+ o, e, s = Open3.capture3("lftp", "-u", "#{user},#{password}", "sftp://#{host}", stdin_data: command)
30
+ unless s.success?
31
+ raise "lftp command `#{command}` failed on sftp://#{host} by #{user}: #{e}"
32
+ end
33
+
34
+ o
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,58 @@
1
+ require "erb"
2
+
3
+ module Rtprov
4
+ class Template
5
+ class RenderingContext
6
+ def initialize(local_vars)
7
+ local_vars.transform_values! {|v| hash_with_accessor(v) }
8
+ local_vars.each do |k, v|
9
+ binding.local_variable_set(k.to_sym, v)
10
+ end
11
+ end
12
+
13
+ def binding
14
+ @binding ||= super
15
+ end
16
+
17
+ private
18
+ # define key name method for hash recursively
19
+ def hash_with_accessor(obj)
20
+ case obj
21
+ when Array
22
+ obj.map {|e| hash_with_accessor(e) }
23
+ when Hash
24
+ obj.each_with_object({}) do |(k, v), h|
25
+ h[k] = hash_with_accessor(v)
26
+ h.singleton_class.define_method k do
27
+ fetch(k)
28
+ end
29
+ end
30
+ else
31
+ obj
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.find(router, name)
37
+ candidates = [
38
+ "templates/#{router}/#{name}.erb",
39
+ "templates/#{name}.erb",
40
+ ]
41
+
42
+ candidates.find do |candidate|
43
+ return new(File.read(candidate)) if File.exist?(candidate)
44
+ end
45
+
46
+ raise "Template `#{name}` not found in #{candidates.inspect}"
47
+ end
48
+
49
+ def initialize(source)
50
+ @erb = ERB.new(source, trim_mode: "-")
51
+ end
52
+
53
+ def render(variables)
54
+ context = RenderingContext.new(variables)
55
+ @erb.result(context.binding)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module Rtprov
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "rtprov/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "rtprov"
9
+ spec.version = Rtprov::VERSION
10
+ spec.authors = ["labocho"]
11
+ spec.email = ["labocho@penguinlab.jp"]
12
+
13
+ spec.summary = "Yamaha router (RTX or NVR series) provisioning tool"
14
+ spec.description = "Yamaha router (RTX or NVR series) provisioning tool"
15
+ spec.homepage = "https://github.com/labocho/rtprov"
16
+ spec.license = "MIT"
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = "https://github.com/labocho/rtprov"
25
+ spec.metadata["changelog_uri"] = "https://github.com/labocho/rtprov/master/CHANGELOG.md"
26
+ else
27
+ raise "RubyGems 2.0 or newer is required to protect against " \
28
+ "public gem pushes."
29
+ end
30
+
31
+ # Specify which files should be added to the gem when it is released.
32
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
33
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
34
+ `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test|spec|features)/}) }
35
+ end
36
+ spec.bindir = "exe"
37
+ spec.executables = spec.files.grep(%r(^exe/)) {|f| File.basename(f) }
38
+ spec.require_paths = ["lib"]
39
+
40
+ spec.add_dependency "reversible_cryptography"
41
+ spec.add_dependency "thor"
42
+
43
+ spec.add_development_dependency "bundler", "~> 2.0"
44
+ spec.add_development_dependency "rake", "~> 10.0"
45
+ spec.add_development_dependency "rubocop", "~> 0.74.0"
46
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rtprov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - labocho
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-09-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: reversible_cryptography
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.74.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.74.0
83
+ description: Yamaha router (RTX or NVR series) provisioning tool
84
+ email:
85
+ - labocho@penguinlab.jp
86
+ executables:
87
+ - rtprov
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rubocop.yml"
93
+ - CHANGELOG.md
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/console
100
+ - bin/setup
101
+ - exe/rtprov
102
+ - lib/rtprov.rb
103
+ - lib/rtprov/cli.rb
104
+ - lib/rtprov/encryption.rb
105
+ - lib/rtprov/initializer.rb
106
+ - lib/rtprov/router.rb
107
+ - lib/rtprov/session.rb
108
+ - lib/rtprov/sftp.rb
109
+ - lib/rtprov/template.rb
110
+ - lib/rtprov/version.rb
111
+ - rtprov.gemspec
112
+ homepage: https://github.com/labocho/rtprov
113
+ licenses:
114
+ - MIT
115
+ metadata:
116
+ allowed_push_host: https://rubygems.org
117
+ homepage_uri: https://github.com/labocho/rtprov
118
+ source_code_uri: https://github.com/labocho/rtprov
119
+ changelog_uri: https://github.com/labocho/rtprov/master/CHANGELOG.md
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubygems_version: 3.0.3
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Yamaha router (RTX or NVR series) provisioning tool
139
+ test_files: []