Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

237 lines (193 sloc) 9.667 kb
#!/usr/bin/env ruby
# this is rz_mk_control_server.rb script
# it is the Microkernel Controller script, and is started as a daemon process using
# the associated rz_mk_controller.rb script
# EMC Confidential Information, protected under EMC Bilateral Non-Disclosure Agreement.
# Copyright © 2012 EMC Corporation, All Rights Reserved
# @author Tom McSweeney
require 'rubygems'
#require 'logger'
require 'net/http'
require 'uri'
require 'open-uri'
require 'json'
require 'yaml'
require 'facter'
require 'razor_microkernel/logging'
require 'razor_microkernel/rz_mk_registration_manager'
require 'razor_microkernel/rz_mk_fact_manager'
require 'razor_microkernel/rz_mk_configuration_manager'
def load_tcl_extensions(config_manager)
# get the URI from the config that points to the YAML file containing
# the list of TCL extensions that we should load, if it doesn't exist,
# then just return (because we don't have any extensions to load)
tcl_ext_list_uri = config_manager.mk_ext_list_uri
return if !tcl_ext_list_uri || (tcl_ext_list_uri =~ URI::regexp).nil?
# and get the TCL mirror URI from the config, if it doesn't exist, then
# we just return (because we don't know where to get the extensions from)
tcl_ext_mirror_uri = config_manager.mk_ext_mirror_uri
return if !tcl_ext_mirror_uri || (tcl_ext_mirror_uri =~ URI::regexp).nil?
# modify the /opt/tcemirror file (so that it uses the mirror given in the
# configuration we just received from the Razor server)'/opt/tcemirror', 'w') { |file|
file.puts tcl_ext_mirror_uri
# get the list of 'TCL Extensions' that should be installed (these will)
# be obtained from a local 'mirror' containing the appropriate 'tcz' files)
ext_list_array = YAML::load(open(tcl_ext_list_uri))
# each extension on that list, load that extension (using the tcl-load command)
has_kernel_modules = false
ext_list_array.each { |extension|
logger.debug "loading #{extension}"
t = %x[sudo -u tc tce-load -iw #{extension}]
has_kernel_modules = true if /open_vm_tools/.match(extension)
# if any of the extensions contained kernel modules, then load those kernel modules
%x[sudo /usr/local/bin/load_kernel_modules.rb] if has_kernel_modules
rescue => e
logger.error e.message
# set up a global variable that will be used in the RazorMicrokernel::Logging mixin
# to determine where to place the log messages from this script
RZ_MK_LOG_PATH = "/var/log/rz_mk_controller.log"
# include the RazorMicrokernel::Logging mixin (which enables logging)
include RazorMicrokernel::Logging
# get a reference to the Configuration Manager instance (a singleton)
config_manager = (RazorMicrokernel::RzMkConfigurationManager).instance
# setup the RzMkFactManager instance (we'll use this later, in our
# RzMkRegistrationManager constructor)
fact_manager ='/tmp/prev_facts.yaml')
# and set the Registration Manager to nil (will update this, below)
registration_manager = nil
# test to see if the configuration file exists
if config_manager.config_file_exists? then
# load the Microkernel Configuration, use the parameters in that
# configuration to setup the Microkernel Controller
# now, load a few items from the configuration manager, first the log
# level that the Microkernel should use
logger.level = config_manager.mk_log_level
# Next, grab the URI for the Razor Server
razor_uri = config_manager.mk_uri
# add the "node register" entry from the configuration map to that URI
# to get the registration URI
registration_uri = razor_uri + config_manager.mk_register_path
logger.debug "registration_uri = #{registration_uri}"
# and add the 'node checkin' entry from the configuration map to that URI
# to get the checkin URI
checkin_uri = razor_uri + config_manager.mk_checkin_path
logger.debug "checkin_uri = #{checkin_uri}"
# next, the time (in secs) to sleep between iterations of the main
# loop (below)
checkin_interval = config_manager.mk_checkin_interval
# next, the maximum amount of time to wait (in secs) the before starting
# the main loop (below); a random number between zero and that amount of
# time will be determined and used to ensure Microkernel instances are
# offset from each other when it comes to tasks like reporting facts to
# the Razor server
checkin_skew = config_manager.mk_checkin_skew
# this parameter defines which facts (by name) should be excluded from the
# map that is reported during node registration
exclude_pattern = config_manager.mk_fact_excl_pattern
logger.debug "exclude_pattern = #{exclude_pattern}"
registration_manager =,
exclude_pattern, fact_manager)
# and load the TCL extensions from the configuration file (if any exist)
checkin_uri = nil
checkin_interval = 30
checkin_skew = 5
# convert the sleep times to milliseconds (for generating random skew value
# and calculation of time remaining in each iteration; these will be to
# the nearest millisecond)
msecs_sleep = checkin_interval * 1000;
max_skew_msecs = checkin_skew * 1000;
# generate a random number between zero and max_skew_msecs (in milliseconds)
# and sleep for that amount of time (in seconds)
rand_secs = rand(max_skew_msecs) / 1000.0 "Sleeping for #{rand_secs} seconds"
idle = 'idle'
# and enter the main event-handling loop
loop do
# grab the current time (used for calculation of the wait time and for
# determining whether or not to register the node if the facts have changed
# later in the event-handling loop)
t1 =
# if the checkin_uri was defined, then send a "checkin" message to the server
if checkin_uri
# Note: as of v0.7.0.0 of the Microkernel, the system is no longer identified using
# a Microkernel-defined UUID value. Instead, the Microkernel reports an array
# containing "hw_id" information to the Razor server and the Razor server uses that
# information to construct the UUID that the system will be (or is) mapped to.
# The array passed through this "hw_id" key in the JSON hash is constructed by the
# FactManager. Currently, it includes a list of all of the network interfaces that
# have names that look like 'eth[0-9]+', but that may change down the line.
hw_id = fact_manager.get_hw_id_array
checkin_uri_string = checkin_uri + "?hw_id=#{hw_id}&last_state=#{idle}" "checkin_uri_string = #{checkin_uri_string}"
uri = URI checkin_uri_string
# then,handle the reply (could include a command that must be handled)
response = Net::HTTP.get(uri)
logger.debug "checkin response => #{response}"
response_hash = JSON.parse(response)
# if error code is 0 ()indicating a successful checkin), then process the response
if response_hash['errcode'] == 0 then
# first, trigger appropriate action based on the command in the response
command = response_hash['response']['command_name']
if command == "acknowledge" then
logger.debug "Received #{command} from #{checkin_uri_string}"
elsif registration_manager && command == "register" then
logger.debug "Register command received, registering the node"
elsif command == "reboot" then
# reboots the node, sense in logging this since the "filesystem"
# is all in memory and will disappear when the reboot happens
%x[sudo reboot now]
# next, check the configuration that is included in the response...
config_map = response_hash['client_config']
if config_map
# check to see if the configuration from the response is different from the current
# Microkernel Controller configuration
if config_manager.mk_config_has_changed?(config_map)
# If it has changed, then post the new configuration to the WEBrick instance
# (which will trigger a restart of this Microkernel Controller instance)
config_map_string = JSON.generate(config_map)
logger.debug "Posting config to WEBrick server => #{config_map_string}"
uri = URI "http://localhost:2156/setMkConfig"
res = Net::HTTP.post_form(uri, config_map_string)
# probably won't ever get here (the reboot from the WEBrick instance will intervene)
# but, just in case...
logger.debug "Response received back => #{res.body}"
# if we haven't saved the facts since we started this iteration, then we
# need to check to see whether or not the facts have changed since our last
# registration; if so, then we need to re-register this node
if registration_manager && t1 > fact_manager.last_saved_timestamp then
logger.error("An exception occurred: #{$!}")
# check to see how much time has elapsed, sleep for the time remaining
# in the msecs_sleep time window
t2 =
msecs_elapsed = (t2 - t1) * 1000
if msecs_elapsed < msecs_sleep then
secs_sleep = (msecs_sleep - msecs_elapsed)/1000.0 "Time remaining: #{secs_sleep} seconds..."
sleep(secs_sleep) if secs_sleep >= 0.0
Jump to Line
Something went wrong with that request. Please try again.