zygote 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/cell_queue.rb +45 -0
- data/lib/chef.rb +24 -0
- data/lib/http.rb +96 -0
- data/lib/memory.rb +26 -0
- data/lib/util.rb +36 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6e95ff9db7b9ed9bf04815b47d7fbc3dd8e96ea1
|
4
|
+
data.tar.gz: 154a5dfd45af2c06da4b6e479483ee9520fb8d1e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d537064fa1d974cae1d02f5a535d32b6da73a8963b1282ff295291226f85df3f8df53f6b2585d75832df5a147ad07483150f4831895bc04b43cd66fb2e01efc3
|
7
|
+
data.tar.gz: 1b81bad1b0c4e4f96f45a708dbe8e73eef2f59ff42ede695de88d1ad525694fba2d53b6a2c823205e44319fe98933d712ef12ef2b86242ebd7d8c14ad2bcbbbc
|
data/lib/cell_queue.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'memory'
|
2
|
+
|
3
|
+
# An entry into the queue
|
4
|
+
class CellQueueEntry < SuperModel::Base
|
5
|
+
include SuperModel::Marshal::Model
|
6
|
+
end
|
7
|
+
|
8
|
+
# A means of storing Cell queue data for a given sku
|
9
|
+
module CellQueue
|
10
|
+
extend self
|
11
|
+
COLLECTION = :assets
|
12
|
+
ARRAY_KEY = :cell_queue
|
13
|
+
Memory.load
|
14
|
+
|
15
|
+
def push(key, data)
|
16
|
+
entry = CellQueueEntry.find_by_name(key)
|
17
|
+
unless entry
|
18
|
+
entry = CellQueueEntry.new(name: key, data: [])
|
19
|
+
entry.save
|
20
|
+
end
|
21
|
+
entry.data << data
|
22
|
+
entry.save
|
23
|
+
Memory.save
|
24
|
+
end
|
25
|
+
|
26
|
+
def shift(key)
|
27
|
+
entry = CellQueueEntry.find_by_name(key)
|
28
|
+
return nil unless entry
|
29
|
+
first = entry.data.shift
|
30
|
+
entry.save
|
31
|
+
Memory.save
|
32
|
+
first
|
33
|
+
end
|
34
|
+
|
35
|
+
def show(key)
|
36
|
+
entry = CellQueueEntry.find_by_name(key)
|
37
|
+
entry ? entry.data : []
|
38
|
+
end
|
39
|
+
|
40
|
+
def purge(key)
|
41
|
+
entry = CellQueueEntry.find_by_name(key)
|
42
|
+
entry.data = [] if entry
|
43
|
+
entry.save if entry
|
44
|
+
end
|
45
|
+
end
|
data/lib/chef.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'chef-provisioner'
|
2
|
+
|
3
|
+
module ChefConfig
|
4
|
+
extend self
|
5
|
+
CLIENT_KEY_PATH = File.expand_path('../../tmp/client.pem', __FILE__).freeze
|
6
|
+
SECRETS_JSON_PATH = File.expand_path('../../config/secrets.json', __FILE__).freeze
|
7
|
+
CHEF_CONFIG_DATA = YAML.load(File.read(File.expand_path('../../config/chef.yml', __FILE__)))['chef'].freeze
|
8
|
+
|
9
|
+
def load
|
10
|
+
make_client_pem
|
11
|
+
ChefProvisioner::Chef.configure(endpoint: CHEF_CONFIG_DATA['endpoint'], key_path: CLIENT_KEY_PATH, client: CHEF_CONFIG_DATA['client'])
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def make_client_pem
|
17
|
+
key_data = JSON.load(File.read(SECRETS_JSON_PATH))['chef_client']
|
18
|
+
FileUtils.mkdir_p(File.dirname(CLIENT_KEY_PATH))
|
19
|
+
File.write(CLIENT_KEY_PATH, key_data)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
ChefConfig.load unless ENV['TESTING']
|
data/lib/http.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'genesisreactor'
|
4
|
+
require 'genesis/protocol/http'
|
5
|
+
require 'active_support/all'
|
6
|
+
|
7
|
+
require 'util'
|
8
|
+
require 'cell_queue'
|
9
|
+
require 'chef'
|
10
|
+
|
11
|
+
# Main HTTP class, handles routing methods
|
12
|
+
# Uses sinatra format (all sinatra docs on routing methods apply)
|
13
|
+
class ZygoteWeb < Genesis::Http::Handler
|
14
|
+
CELL_CONFIG = YAML.load(File.read(File.expand_path('../../config/cells.yml', __FILE__))).freeze
|
15
|
+
|
16
|
+
# Requested by iPXE on boot, chains into /boot.
|
17
|
+
# This enables us to customize what details we want iPXE to send us
|
18
|
+
# The iPXE undionly.kpxe should contain an embedded script to call this URL
|
19
|
+
get '/' do
|
20
|
+
body { erb :boot }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Chainload the primary menu
|
24
|
+
get '/chain' do
|
25
|
+
# Clean params into a simple hash
|
26
|
+
cleaned = clean_params(params.to_h)
|
27
|
+
# Add the request ip into the params
|
28
|
+
ip = request.ip == '127.0.0.1' ? @env['HTTP_X_FORWARDED_FOR'] : request.ip
|
29
|
+
ip = '127.0.0.1' if (ENV['TESTING'] || ip.nil? || ip.empty?)
|
30
|
+
cleaned['ip'] = ip
|
31
|
+
# Compute SKU from parameters
|
32
|
+
sku = compute_sku(cleaned['manufacturer'], cleaned['serial'], cleaned['board-serial'])
|
33
|
+
cleaned['sku'] = sku
|
34
|
+
# Check if there are is any queued data for this SKU, and if so, merge it in to params
|
35
|
+
queued_data = CellQueue.shift(sku)
|
36
|
+
cleaned.merge!(queued_data) if queued_data
|
37
|
+
@channel << cleaned
|
38
|
+
body { erb :menu, locals: { opts: CELL_CONFIG.merge('params' => cleaned || {}) } }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Render an action for a particular cell
|
42
|
+
get %r{/cell/(?<cell>\S*)/(?<action>\S*)} do
|
43
|
+
# Clean params into a simple hash
|
44
|
+
cleaned = clean_params(params.to_h)
|
45
|
+
# Add the cell to the parameters
|
46
|
+
cell = cleaned['cell']
|
47
|
+
# Merge the cleaned params in with any cell options
|
48
|
+
cell_opts = CELL_CONFIG['index']['cells'][cell] || {}
|
49
|
+
opts = cell_opts.merge('params' => cleaned || {})
|
50
|
+
@channel << opts # for debugging
|
51
|
+
body { erb :"#{cell}/#{cleaned['action']}".to_sym, locals: { opts: opts } }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Show the queue for a SKU
|
55
|
+
get %r{/queue/(?<sku>\S*)} do
|
56
|
+
body { JSON.pretty_generate(CellQueue.show(params['sku'])) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Delete the queue for a SKU
|
60
|
+
delete '/queue' do
|
61
|
+
CellQueue.purge(params['sku'])
|
62
|
+
body { JSON.pretty_generate(CellQueue.show(params['sku'])) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Enable push cells (with optional data) to the cell queue for a SKU
|
66
|
+
post %r{/queue/(?<sku>\S*)/(?<cell>\S*)} do
|
67
|
+
# Clean params into a simple hash
|
68
|
+
cleaned = clean_params(params.to_h)
|
69
|
+
# Enqueue some data for this sku
|
70
|
+
sku = cleaned.delete('sku')
|
71
|
+
CellQueue.push(sku, cleaned)
|
72
|
+
body { JSON.pretty_generate(CellQueue.show(sku)) }
|
73
|
+
end
|
74
|
+
|
75
|
+
subscribe do |args|
|
76
|
+
puts args if ENV['DEBUG']
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def zygote
|
81
|
+
zygote = Genesis::Reactor.new(
|
82
|
+
threads: 1000,
|
83
|
+
protocols: {
|
84
|
+
Genesis::Http::Protocol => 7000
|
85
|
+
},
|
86
|
+
handlers: [ZygoteWeb],
|
87
|
+
views: [File.join(Dir.pwd, 'views'), File.join(Dir.pwd, 'cells')],
|
88
|
+
debug: ENV['DEBUG']
|
89
|
+
)
|
90
|
+
zygote
|
91
|
+
end
|
92
|
+
|
93
|
+
if ENV['DEBUG']
|
94
|
+
$stdout.sync = true
|
95
|
+
$stderr.sync = true
|
96
|
+
end
|
data/lib/memory.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'supermodel'
|
5
|
+
|
6
|
+
# A simple means of persistence
|
7
|
+
# This can easily be swapped out for redis, but file-based is simpler and good enough for now
|
8
|
+
# https://github.com/maccman/supermodel/blob/master/README
|
9
|
+
module Memory
|
10
|
+
extend self
|
11
|
+
DATABASE_PATH = (ENV['DATABASE_PATH'] || File.expand_path('../../data/memory.db', __FILE__)).freeze
|
12
|
+
SuperModel::Marshal.path = DATABASE_PATH
|
13
|
+
|
14
|
+
def save
|
15
|
+
FileUtils.mkdir_p(File.dirname(DATABASE_PATH)) # FIXME - don't make if it already exists
|
16
|
+
SuperModel::Marshal.dump
|
17
|
+
end
|
18
|
+
|
19
|
+
def load
|
20
|
+
SuperModel::Marshal.load
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
at_exit do
|
25
|
+
Memory.save
|
26
|
+
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
def compute_sku(vendor, serial, board_serial)
|
4
|
+
# Sanitize params
|
5
|
+
strip_pattern = /[^-^:\p{Alnum}]/
|
6
|
+
serial = (serial || '').gsub(strip_pattern, '')
|
7
|
+
vendor = (vendor || '').gsub(strip_pattern, '')
|
8
|
+
board_serial = (board_serial || '').gsub(strip_pattern, '')
|
9
|
+
|
10
|
+
serial = board_serial unless board_serial.empty?
|
11
|
+
|
12
|
+
case vendor
|
13
|
+
when 'DellInc'
|
14
|
+
sku = 'DEL'
|
15
|
+
when 'Supermicro'
|
16
|
+
sku = 'SPM'
|
17
|
+
else
|
18
|
+
sku = 'UKN' # unknown manufacturer
|
19
|
+
end
|
20
|
+
|
21
|
+
sku = "#{sku}-#{serial}"
|
22
|
+
sku
|
23
|
+
end
|
24
|
+
|
25
|
+
def clean_params(params)
|
26
|
+
params.delete_if { |x, _| x == 'splat' || x == 'captures' }
|
27
|
+
params
|
28
|
+
end
|
29
|
+
|
30
|
+
def my_ip
|
31
|
+
Socket.ip_address_list.find{|x| x.ipv4? && !x.ipv4_loopback?}.ip_address
|
32
|
+
end
|
33
|
+
|
34
|
+
def discover_domain
|
35
|
+
Socket.gethostname.split('.')[1..-1].join('.')
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zygote
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dale Hamel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Automate baremetal server actions with iPXE
|
14
|
+
email: dale.hamel@srvthe.net
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/cell_queue.rb
|
20
|
+
- lib/chef.rb
|
21
|
+
- lib/http.rb
|
22
|
+
- lib/memory.rb
|
23
|
+
- lib/util.rb
|
24
|
+
homepage: http://rubygems.org/gems/zygote
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 2.4.6
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: Differentiate servers with iPXE
|
48
|
+
test_files: []
|
49
|
+
has_rdoc:
|