arnold 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +92 -0
- data/bin/arnold +82 -0
- data/doc/certs/README +1 -0
- data/doc/certs/server.crt +15 -0
- data/doc/certs/server.key +15 -0
- data/doc/config.yaml +16 -0
- data/doc/postjson.rb +42 -0
- data/lib/arnold/controller.rb +6 -0
- data/lib/arnold/controller/cli.rb +65 -0
- data/lib/arnold/controller/web.rb +31 -0
- data/lib/arnold/monkeypatch.rb +14 -0
- data/lib/arnold/node.rb +86 -0
- data/lib/arnold/node_manager.rb +113 -0
- data/lib/arnold/provisioner.rb +23 -0
- data/lib/arnold/provisioner/cloud_provisioner.rb +24 -0
- data/lib/arnold/provisioner/null.rb +14 -0
- data/lib/arnold/server.rb +110 -0
- data/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/public/jquery-ui.css +464 -0
- data/public/jquery.js +9301 -0
- data/public/scripts.js +21 -0
- data/public/style.css +36 -0
- data/views/error.erb +8 -0
- data/views/layout.erb +21 -0
- data/views/new.erb +26 -0
- data/views/node.erb +42 -0
- data/views/nodes.erb +14 -0
- metadata +104 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2013 Puppet Labs, info@puppetlabs.com
|
2
|
+
Copyright (c) 2013 FBL Financial, puppet@fblfinancial.com
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
Introduction
|
2
|
+
============
|
3
|
+
|
4
|
+
### Arnold: *the provisionator*
|
5
|
+
|
6
|
+
This is the start of a Razor provisioning system.
|
7
|
+
|
8
|
+
Currently, all it does is provide a web service that will read & write YAML files
|
9
|
+
and a few functions to read them and apply classes to a node.
|
10
|
+
|
11
|
+
Very soon, I plan to provide Razor functionality to spin up new instances after
|
12
|
+
classifying them, and then it will be a simplistic self service provisioner.
|
13
|
+
|
14
|
+
Configuration
|
15
|
+
=============
|
16
|
+
|
17
|
+
* Installing
|
18
|
+
* It's a Puppet module, yo! Copy the `arnold` subdirectory to your modulepath.
|
19
|
+
* Alternately, you can install by hand by running the little installer script.
|
20
|
+
* Setup the server
|
21
|
+
1. Classify your server with `arnold::provisionator` and apply.
|
22
|
+
2. Configure by editing `/etc/arnold/config.yaml`
|
23
|
+
* You will probably want to point the `datadir` to wherever you've configured Hiera to use.
|
24
|
+
* You may configure Arnold to reuse Puppet certs if you wish.
|
25
|
+
* If you choose to generate your own SSL certs, drop them in /etc/arnold/certs
|
26
|
+
* `openssl genrsa -out server.key 1024`
|
27
|
+
* `openssl req -new -key server.key -out server.csr`
|
28
|
+
* `openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt`
|
29
|
+
3. Point a web browser at the configured port
|
30
|
+
* Clicky clicky
|
31
|
+
* List nodes, create nodes, modify nodes, etc
|
32
|
+
4. Enable arnold for clients:
|
33
|
+
* In `site.pp` on your master, outside of any node definitions, simply call the `arnold()` function.
|
34
|
+
* Alternately, you can classify your nodes with `arnold`
|
35
|
+
* If you place this class in your default group, it will automatically be applied to all nodes.
|
36
|
+
|
37
|
+
Provisioner backend configuration
|
38
|
+
=============
|
39
|
+
|
40
|
+
Currently only the CloudProvisioner provisioning backend exits. You can enable it by
|
41
|
+
adding a stanza like this to your `config.yaml`
|
42
|
+
|
43
|
+
backend: CloudProvisioner
|
44
|
+
keyfile: ~/.ssh/private_key.pem
|
45
|
+
enc_password: <password>
|
46
|
+
|
47
|
+
If a backend is not configured, Arnold will not perform any provisioning actions.
|
48
|
+
It is entirely useful like this, as it will instead only manage the Hiera datafiles.
|
49
|
+
|
50
|
+
Limitations
|
51
|
+
============
|
52
|
+
|
53
|
+
* It does not currently manage parameterized classes.
|
54
|
+
* Razor support is not yet included.
|
55
|
+
|
56
|
+
Contact
|
57
|
+
=======
|
58
|
+
|
59
|
+
* Author: Ben Ford
|
60
|
+
* Email: ben.ford@puppetlabs.com
|
61
|
+
* Twitter: @binford2k
|
62
|
+
* IRC (Freenode): binford2k
|
63
|
+
|
64
|
+
Credit
|
65
|
+
=======
|
66
|
+
|
67
|
+
The development of this code was sponsored by FBL Financial.
|
68
|
+
|
69
|
+
License
|
70
|
+
=======
|
71
|
+
|
72
|
+
Copyright (c) 2013 Puppet Labs, info@puppetlabs.com
|
73
|
+
Copyright (c) 2013 FBL Financial, puppet@fblfinancial.com
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
"Software"), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
89
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
90
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
91
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
92
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/arnold
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'yaml'
|
5
|
+
require 'optparse'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
require 'arnold/node'
|
9
|
+
require 'arnold/node_manager'
|
10
|
+
require 'arnold/monkeypatch'
|
11
|
+
require 'arnold/controller'
|
12
|
+
|
13
|
+
cmdlineopts = {}
|
14
|
+
optparse = OptionParser.new { |opts|
|
15
|
+
opts.banner = "Usage : arnold [-c <confdir>] [-d]
|
16
|
+
|
17
|
+
Runs the arnold daemon.
|
18
|
+
|
19
|
+
"
|
20
|
+
|
21
|
+
opts.on("-b", "--backend BACKEND", "Choose an alternate provisioning backend.") do |opt|
|
22
|
+
cmdlineopts[:backend] = opt
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-c CONFIG", "--config CONFIG", "Choose an alternate config file. Defaults to /etc/arnold/config.yaml") do |opt|
|
26
|
+
configfile = opt
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-d", "--debug", "Run in the foreground and display debugging messages") do
|
30
|
+
# Separate options so daemonize = false doesn't force debug
|
31
|
+
cmdlineopts[:debug] = true
|
32
|
+
cmdlineopts[:daemonize] = false
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.separator('')
|
36
|
+
|
37
|
+
opts.on("-h", "--help", "Displays this help") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
}
|
42
|
+
optparse.parse!
|
43
|
+
|
44
|
+
# Load default configuration data, deferring to command line overrides
|
45
|
+
configfile ||= '/etc/arnold/config.yaml'
|
46
|
+
|
47
|
+
begin
|
48
|
+
$CONFIG = YAML.load_file(configfile)
|
49
|
+
$CONFIG.merge!(cmdlineopts)
|
50
|
+
rescue Errno::ENOENT => e
|
51
|
+
puts "Config file doesn't exist; loading defaults! (#{e})" if cmdlineopts[:debug]
|
52
|
+
$CONFIG={}
|
53
|
+
end
|
54
|
+
|
55
|
+
$CONFIG[:docroot] ||= File.dirname(__FILE__) + '/../' # well isn't this ugly
|
56
|
+
$CONFIG[:backend] ||= 'Null'
|
57
|
+
$CONFIG[:datadir] ||= '/etc/arnold/data'
|
58
|
+
$CONFIG[:sslcert] ||= '/etc/arnold/certs/server.crt'
|
59
|
+
$CONFIG[:sslkey] ||= '/etc/arnold/certs/server.key'
|
60
|
+
$CONFIG[:port] ||= 9090
|
61
|
+
$CONFIG[:enc_server] ||= 'localhost'
|
62
|
+
$CONFIG[:enc_port] ||= 443
|
63
|
+
$CONFIG[:enc_user] ||= 'console'
|
64
|
+
|
65
|
+
begin
|
66
|
+
# load up our provisioning backend
|
67
|
+
require "arnold/provisioner/#{$CONFIG[:backend].to_underscore}"
|
68
|
+
$CONFIG[:provisioner] = Arnold::Provisioner::const_get($CONFIG[:backend]).new
|
69
|
+
rescue LoadError, NameError => e
|
70
|
+
puts "No such provisioning backend, loading null backend! (#{e})"
|
71
|
+
require 'arnold/provisioner/null'
|
72
|
+
$CONFIG[:provisioner] = Arnold::Provisioner::Null.new
|
73
|
+
end
|
74
|
+
|
75
|
+
case ARGV[0]
|
76
|
+
when 'serve'
|
77
|
+
Arnold::Controller::Web.new
|
78
|
+
|
79
|
+
else
|
80
|
+
Arnold::Controller::Cli.new(ARGV)
|
81
|
+
|
82
|
+
end
|
data/doc/certs/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Place certificates in this directory
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICRzCCAbACCQDZGL+AiHPpyjANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJV
|
3
|
+
UzEPMA0GA1UECBMGT3JlZ29uMREwDwYDVQQHEwhQb3J0bGFuZDEUMBIGA1UEChML
|
4
|
+
UHVwcGV0IExhYnMxCzAJBgNVBAsTAlBTMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcN
|
5
|
+
MTIwOTE5MTcxMTQyWhcNMTMwOTE5MTcxMTQyWjBoMQswCQYDVQQGEwJVUzEPMA0G
|
6
|
+
A1UECBMGT3JlZ29uMREwDwYDVQQHEwhQb3J0bGFuZDEUMBIGA1UEChMLUHVwcGV0
|
7
|
+
IExhYnMxCzAJBgNVBAsTAlBTMRIwEAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZI
|
8
|
+
hvcNAQEBBQADgY0AMIGJAoGBAKjECcBJqQx5G479gtFVSF7M9noXEcTG6tV/Z3SW
|
9
|
+
h/N5PjZzovcc9tKzCRfeCzVGupkUblkxwhjP+gWSjcPoioIvG1twfQIDuiG6wtH5
|
10
|
+
FfS2q8creJZwd9OxGMTb4mkmfaY+88N9vt0a+OyLE6L9Cw6+CoB2XNAwgcNWKdzs
|
11
|
+
QzfPAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAbItcPsOqCqktv+TuXrCgiYdmQjEl
|
12
|
+
R/xKqDUGSNkkUJCEjmff6VJslPzdteIkv42trJZMbbQY2SR1eylt7/AmLXmSqEJh
|
13
|
+
ivnAF3MCAg59RF3P5RoqFmjUJZcsoGZp7v6d5y2/HB712JKmiohIWj82H6iayW+k
|
14
|
+
43jfo4DvdfNUL7I=
|
15
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIICXQIBAAKBgQCoxAnASakMeRuO/YLRVUhezPZ6FxHExurVf2d0lofzeT42c6L3
|
3
|
+
HPbSswkX3gs1RrqZFG5ZMcIYz/oFko3D6IqCLxtbcH0CA7ohusLR+RX0tqvHK3iW
|
4
|
+
cHfTsRjE2+JpJn2mPvPDfb7dGvjsixOi/QsOvgqAdlzQMIHDVinc7EM3zwIDAQAB
|
5
|
+
AoGAWsFJaSFziiSagFOuBLpy96ALL+61/HboFDW2QckthO3/WbLnwTHPPdFPo4kh
|
6
|
+
x92oPOfyy35pnYRCNLryB5dG3Al9b6x9G5wx5PqXASShFhgjrUYTA0bPnAeMM7po
|
7
|
+
b8x5rMssVhCPYYxmrKVyTEsmzL8EhGcEArEyFfL4JSaEA0ECQQDaMies6EeTeQ3v
|
8
|
+
v2hJUiknC4T++RfAubo1/mk4y4U7WUbmLGUXOE6txCbqRMs7HUZrL/kDmxORWudo
|
9
|
+
XSevQSPvAkEAxgF2WFMR0C1PpMbKaE8CSQfAOawt9u3eJ+TSRBHa3k+IRb8dg2OQ
|
10
|
+
yfbEc1YvUBE3Ycz8eOu5WBzoFLf7uq3KIQJAToNJn4AdcUVX7HL1dZyozjHo805y
|
11
|
+
a5jpFlCrUBJ7qHVhe6Vx4r8SIJi6YAXNE0JfemZStidxDRamufj7NKa95QJBALIG
|
12
|
+
po0LQzzVQIJ6aYoXX4qh+WbhNAKMI+3iglrJYuv2viNXjgWQA6JSyJaaqrdmg1Df
|
13
|
+
qTBfYKmkc9YNBbv2fYECQQDMSMx9gkDu/Rhv8fF8y4gUrVqSh8/aSWqrbaEQwEkp
|
14
|
+
MPbCuYg/dh0Epb+btmed5+b3zcQImlPccP0JZGs0nFa4
|
15
|
+
-----END RSA PRIVATE KEY-----
|
data/doc/config.yaml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
:sslcert: /etc/arnold/certs/server.crt
|
3
|
+
:sslkey: /etc/arnold/certs/server.key
|
4
|
+
:datadir: /etc/arnold/data
|
5
|
+
|
6
|
+
:user: admin
|
7
|
+
:password: admin
|
8
|
+
|
9
|
+
:daemonize: false
|
10
|
+
:port: 9090
|
11
|
+
|
12
|
+
:classes:
|
13
|
+
apache: Manage the Apache webserver
|
14
|
+
mysql: Manage the MySQL database
|
15
|
+
ntp::server: Install the NTP server
|
16
|
+
ntp::client: Install the NTP client and configure the node to query the internal NTP server
|
data/doc/postjson.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# A quick 'n dirty example of a simple script to exercise Arnold's REST API
|
4
|
+
|
5
|
+
require 'net/https'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'json'
|
8
|
+
require 'uri'
|
9
|
+
|
10
|
+
user = 'admin'
|
11
|
+
pass = 'admin'
|
12
|
+
server = 'localhost'
|
13
|
+
|
14
|
+
def provision()
|
15
|
+
# Call VMware APIs to create a new machine, configure, and boot it.
|
16
|
+
# Return its MAC address.
|
17
|
+
return '00:0C:29:D1:03:A4'
|
18
|
+
end
|
19
|
+
|
20
|
+
macaddr = provision()
|
21
|
+
|
22
|
+
payload = {
|
23
|
+
'macaddr' => macaddr,
|
24
|
+
'name' => 'this.is.another.brand.new.system',
|
25
|
+
'parameters' => {
|
26
|
+
'booga' => 'wooga',
|
27
|
+
'fiddle' => 'faddle',
|
28
|
+
},
|
29
|
+
'classes' => [ 'test', 'mysql', 'ntp' ],
|
30
|
+
}.to_json
|
31
|
+
|
32
|
+
uri = URI.parse("https://#{server}:9090/api/v1/create")
|
33
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
34
|
+
http.use_ssl = true
|
35
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
36
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
37
|
+
request.add_field('Content-Type', 'application/json')
|
38
|
+
request.basic_auth(user, pass)
|
39
|
+
request.body = payload
|
40
|
+
response = http.request(request)
|
41
|
+
puts "Response #{response.code} #{response.message}: #{response.body}"
|
42
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'arnold/node'
|
2
|
+
require 'arnold/node_manager'
|
3
|
+
|
4
|
+
module Arnold
|
5
|
+
module Controller
|
6
|
+
class Cli
|
7
|
+
|
8
|
+
def initialize(args)
|
9
|
+
@manager = Arnold::NodeManager.new
|
10
|
+
|
11
|
+
case args[0]
|
12
|
+
when "help"
|
13
|
+
usage
|
14
|
+
when "list"
|
15
|
+
listnodes
|
16
|
+
exit 0
|
17
|
+
when "new"
|
18
|
+
args.shift
|
19
|
+
@data = {}
|
20
|
+
args.each do |arg|
|
21
|
+
name, value = arg.split("=")
|
22
|
+
@data[name] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
node = Arnold::Node.new(nil,
|
27
|
+
@data['name'],
|
28
|
+
@data['macaddr'],
|
29
|
+
Arnold::Node.munge(@data, :params),
|
30
|
+
@data['classes'].split(','))
|
31
|
+
@manager.write(node)
|
32
|
+
|
33
|
+
$CONFIG[:provisioner].provision(node)
|
34
|
+
rescue RuntimeError => e
|
35
|
+
puts "Whoops: #{e}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
puts "WAT"
|
39
|
+
usage
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def listnodes
|
44
|
+
nodes = @manager.loadall
|
45
|
+
puts
|
46
|
+
puts "________GUID______________________Name____________________MAC Address___"
|
47
|
+
nodes.each do |node|
|
48
|
+
printf "%18s │ %30s │ %18s\n", node.guid, node.name, node.macaddr
|
49
|
+
end
|
50
|
+
puts
|
51
|
+
end
|
52
|
+
|
53
|
+
def usage
|
54
|
+
puts
|
55
|
+
puts "Usage:"
|
56
|
+
puts " * arnold help"
|
57
|
+
puts " * arnold list"
|
58
|
+
puts " * arnold new [name=<name>] [macaddr=<macaddr>] [template=<template>] [group=<group>] [classes=<class1,class2,...>] [param1=value1]..."
|
59
|
+
puts
|
60
|
+
exit 1
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'webrick'
|
3
|
+
require 'webrick/https'
|
4
|
+
require 'openssl'
|
5
|
+
|
6
|
+
require 'arnold/server'
|
7
|
+
|
8
|
+
module Arnold
|
9
|
+
module Controller
|
10
|
+
class Web
|
11
|
+
def initialize
|
12
|
+
opts = {
|
13
|
+
:Port => $CONFIG[:port] || 8080,
|
14
|
+
:Logger => WEBrick::Log::new($stderr, WEBrick::Log::DEBUG),
|
15
|
+
:ServerType => $CONFIG[:daemonize] ? WEBrick::Daemon : WEBrick::SimpleServer,
|
16
|
+
:DocumentRoot => $CONFIG[:docroot],
|
17
|
+
:SSLEnable => true,
|
18
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
19
|
+
:SSLCertificate => OpenSSL::X509::Certificate.new( File.open($CONFIG[:sslcert]).read),
|
20
|
+
:SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open($CONFIG[:sslkey]).read),
|
21
|
+
:SSLCertName => [ [ "CN",WEBrick::Utils::getservername ] ]
|
22
|
+
}
|
23
|
+
|
24
|
+
# now it's off to the races!
|
25
|
+
Rack::Handler::WEBrick.run(Arnold::Server, opts) do |server|
|
26
|
+
[:INT, :TERM].each { |sig| trap(sig) { server.stop } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
if not String.method_defined? :to_underscore
|
2
|
+
class String
|
3
|
+
# ruby mutation methods have the expectation to return self if a mutation
|
4
|
+
# occurred, nil otherwise.
|
5
|
+
# (see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-gsub-21)
|
6
|
+
def to_underscore!
|
7
|
+
gsub!(/(.)([A-Z])/,'\1_\2') && downcase!
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_underscore
|
11
|
+
dup.tap { |s| s.to_underscore! }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/arnold/node.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Arnold
|
2
|
+
class Node
|
3
|
+
attr_reader :guid, :name, :macaddr, :parameters, :classes
|
4
|
+
@@reserved_params = ['guid', 'name', 'macaddr', 'classes']
|
5
|
+
|
6
|
+
def initialize(guid=nil, name=nil, macaddr=nil, parameters={}, classes = [])
|
7
|
+
# if no guid, this is intended to be a new node
|
8
|
+
self.guid = guid
|
9
|
+
self.name = name
|
10
|
+
self.macaddr = macaddr
|
11
|
+
self.parameters = parameters
|
12
|
+
self.classes = classes
|
13
|
+
end
|
14
|
+
|
15
|
+
def guid=(g)
|
16
|
+
@guid = validate(g, :filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
def name=(n)
|
20
|
+
@name = validate(n, :filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
def macaddr=(m)
|
24
|
+
@macaddr = validate(m, :macaddr)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parameters=(p)
|
28
|
+
@parameters = validate(p, :params)
|
29
|
+
end
|
30
|
+
|
31
|
+
def classes=(c)
|
32
|
+
@classes = validate(c, :classes)
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns classes and descriptions for classes enabled or disabled
|
36
|
+
def enabled
|
37
|
+
return {} if @classes.nil?
|
38
|
+
$CONFIG[:classes].select { |name, desc| @classes.include? name }
|
39
|
+
end
|
40
|
+
|
41
|
+
def disabled
|
42
|
+
return $CONFIG[:classes] if @classes.nil?
|
43
|
+
$CONFIG[:classes].reject { |name, desc| @classes.include? name }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raise exceptions if the given condition fails
|
47
|
+
#
|
48
|
+
def validate(value, type=:exists)
|
49
|
+
case type
|
50
|
+
when :classes
|
51
|
+
return if value.nil?
|
52
|
+
raise "Invalid type: #{value.class}" if not value.kind_of?(Array)
|
53
|
+
value.each { |n| raise "Invalid class: #{n}" if not $CONFIG[:classes].has_key?(n) }
|
54
|
+
|
55
|
+
when :params
|
56
|
+
return if value.nil?
|
57
|
+
raise "Invalid type: #{value.class}" if not value.kind_of?(Hash)
|
58
|
+
@@reserved_params.each { |n| raise "Invalid parameter: #{n}" if value.has_key?(n) }
|
59
|
+
|
60
|
+
when :macaddr
|
61
|
+
return if value.nil?
|
62
|
+
value.upcase!
|
63
|
+
raise "Invalid MAC address: #{value}" if not value =~ /^(([0-9A-F]{2}[:-]){5}([0-9A-F]{2}))?$/
|
64
|
+
|
65
|
+
when :filename
|
66
|
+
return if value.nil?
|
67
|
+
raise "Invalid name: #{value}" if not value =~ /^([^\/])*$/
|
68
|
+
|
69
|
+
when :exists
|
70
|
+
raise "Value does not exist." if (value.nil? || value.empty?)
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
return value
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.munge(value, type=:upcase)
|
78
|
+
case type
|
79
|
+
when :upcase
|
80
|
+
return value.nil? ? nil : value.upcase
|
81
|
+
when :params
|
82
|
+
return value.reject { |name, val| @@reserved_params.include? name }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|