#!/usr/bin/env ruby abort "Please use Ruby 1.9.3 or higher" if RUBY_VERSION < "1.9.3" require 'gli' require 'omf_ec' $stdout.sync = true include GLI::App include OmfEc program_desc "Run a command on the testbed(s)" version OmfEc::VERSION desc "Debug mode (printing debug logging messages)" switch [:d, :debug] desc "URI for communication layer" arg_name "URI" default_value "xmpp://localhost" flag [:u, :uri] desc "Debug XMPP traffic mode (include XMPP debug logging messages under debug mode)." switch [:x, :xmpp] desc "Root certificate file" arg_name "cert", :optional flag [:root_cert] desc "Your certificate" arg_name "cert", :optional flag [:cert] desc "Your private key" arg_name "key", :optional flag [:key] desc "Log file directory" arg_name "directory" default_value "/tmp" flag [:log_file_dir] config_file(".config/omf_ec.yml") desc "Execute an experiment script" arg_name "path_to_script_file [-- --experiment_property value]" command :exec do |c| c.desc "Experiment name" c.arg_name "experiment_name" c.flag [:e, :experiment] c.desc "Default OML URI to use for collecting measurements" c.arg_name "uri" c.flag [:oml_uri] c.desc "Check script version (you need to define OMF_VERSIONS in your script" c.switch "version_check" c.action do |global_options, options, args| help_now! "Missing experiment script" if args[0].nil? help_now! "Experiment script not found" unless File.exist?(args[0]) # User-provided command line values for Experiment Properties cannot be # set here as the propertties have not been defined yet by the experiment. # Thus just pass them to the experiment, which will be responsible # for setting them later properties = {} if args.size > 1 exp_properties = args[1..-1] exp_properties.in_groups_of(2) do |p| unless p[0] =~ /^--(.+)/ && !p[1].nil? help_now! "Malformatted properties '#{exp_properties.join(' ')}'" else properties[$1.to_sym] = p[1].ducktype end end OmfEc.experiment.cmdline_properties = properties end # FIXME this loading script is way too simple load_exp(args[0], global_options, options, properties) end end desc "Load an image onto the nodes" command :load do |c| #c.desc "use this testbed configuration in OMF 5 EC config file" #c.arg_name "AGGREGATE" #c.flag [:c, :config], :default_value => "default" c.desc "comma-separated list of nodes to image" c.arg_name "TOPOLOGY" c.flag [:t, :topology], :default_value => "system:topo:all" c.desc "disk image to load" c.arg_name "IMAGE" c.flag [:i, :image], :default_value => "baseline.ndz" c.desc "seconds to wait for the imaging process to complete" c.arg_name "TIMEOUT" c.flag [:o, :timeout], :default_value => "800" c.desc "resize the first partition to SIZE GB or to maximum size if SIZE=0 "+ "or leave x percent of free space if SIZE=x%" c.arg_name "SIZE" c.flag [:r, :resize] c.desc "Path where the resulting Topologies should be saved" c.arg_name "PATH" c.flag [:outpath], :default_value => "/tmp" c.desc "Prefix to use for naming the resulting Topologies (default is your experiment ID)" c.arg_name "PREFIX" c.flag [:outprefix] c.action do |global_options, options, args| @cmd = "USER=#{ENV['USER']} omf-5.4 load -t #{options[:t]} -i #{options[:i]} " @cmd += "-o #{options[:o]} --outpath #{options[:outpath]} " @cmd += "-r #{options[:r]} " if options[:r] @cmd += "--outprefix #{options[:outprefix]} " if options[:outprefix] load_exp(@testbed_exp_path, global_options, options) end end desc "Save an image of a node" command :save do |c| #c.desc "use this testbed configuration in OMF 5 EC config file" #c.arg_name "AGGREGATE" #c.flag [:c, :config], :default_value => "default" c.desc "node to save from" c.arg_name "NODE" c.flag [:n, :node] c.desc "resize the first partition to SIZE GB or to maximum size if SIZE=0 "+ "or leave x percent of free space if SIZE=x%" c.arg_name "SIZE" c.flag [:r, :resize] c.action do |global_options, options, args| @cmd = "USER=#{ENV['USER']} omf-5.4 save " @cmd += "-n #{options[:n]} " if options[:n] @cmd += "-r #{options[:r]} " if options[:r] load_exp(@testbed_exp_path, global_options, options) end end desc "Return the status of the nodes" command :stat do |c| c.desc "use this testbed configuration in OMF 5 EC config file" c.arg_name "AGGREGATE" c.flag [:c, :config], :default_value => "default" c.desc "comma-separated list of nodes to image" c.arg_name "TOPOLOGY" c.flag [:t, :topology], :default_value => "system:topo:all" c.desc "print a summary of the node status for the testbed" c.switch [:s, :summary] c.action do |global_options, options, args| @cmd = "omf-5.4 stat -c #{options[:c]} -t #{options[:t]} " @cmd += "-s" if options[:s] load_exp(@testbed_exp_path, global_options, options) end end desc "Power on/off, reset or reboot the nodes" command :tell do |c| c.desc "use this testbed configuration in OMF 5 EC config file" c.arg_name "AGGREGATE" c.flag [:c, :config], :default_value => "default" c.desc "comma-separated list of nodes to image" c.arg_name "TOPOLOGY" c.flag [:t, :topology], :default_value => "system:topo:all" c.desc " 'on' turn node(s) ON - 'offs' turn node(s) OFF (soft) - 'offh' turn node(s) OFF (hard) - 'reboot' reboots node(s) (soft) - 'reset' resets node(s) (hard)" c.arg_name "ACTION" c.flag [:a, :action] c.action do |global_options, options, args| @cmd = "omf-5.4 tell -c #{options[:c]} -t #{options[:t]} " @cmd += "-a #{options[:a]} " if options[:a] load_exp(@testbed_exp_path, global_options, options) end end on_error do |exception| true end pre do |global_options, command, options, args| unless global_options[:uri] help_now! "Incomplete options. Need communication URI" end # Import private key if global_options[:private_key] OmfCommon::Key.instance.import(global_options[:private_key]) end # Check version if options[:check] File.open(args[0], 'r') do |f| f.read.chomp.match(/OMF_VERSIONS\W*=\W*(.*)/) versions = $1 unless versions && versions.split(',').include?(OmfCommon::PROTOCOL_VERSION) raise StandardError, "Could not find compatibile protocol version number in your script" end end end include OmfEc::DSL OmfEc.experiment.name = options[:experiment] if options[:experiment] OmfEc.experiment.oml_uri = options[:oml_uri] if options[:oml_uri] @testbed_exp_path = File.join(OmfEc.lib_root, "omf_ec/backward/exp/testbed.rb") end def setup_logging(global_options = {}) if global_options[:xmpp] require 'blather' Blather.logger = logger end unless global_options[:debug] Logging.consolidate 'OmfCommon', 'OmfEc', 'OmfRc' end # FIXME this should go to common setup if global_options[:log_file_dir] && File.exist?(global_options[:log_file_dir]) Logging.logger.root.add_appenders( Logging.appenders.file( "#{global_options[:log_file_dir]}/#{OmfEc.experiment.id}.log", :layout => Logging.layouts.pattern(:date_pattern => '%F %T %z', :pattern => '[%d] %-5l %c: %m\n'))) end end def load_exp(exp_path, global_options = {} , options = {}, properties = {}) begin if global_options[:root_cert] && File.exist?(global_options[:root_cert]) root = OmfCommon::Auth::Certificate.create_from_x509(File.read(global_options[:root_cert])) end if global_options[:cert] && File.exist?(global_options[:cert]) && global_options[:key] && File.exist?(global_options[:key]) entity = OmfCommon::Auth::Certificate.create_from_x509(File.read(global_options[:cert]), File.read(global_options[:key])) end opts = { communication: { url: global_options[:uri] }, eventloop: { type: :em }, logging: { level: { default: global_options[:debug] ? 'debug' : 'info' }, appenders: { stdout: { date_pattern: '%H:%M:%S', pattern: '%d %-5l %c{2}: %m\n', color_scheme: 'default' } } } } opts[:communication][:auth] = { certs: [ root.to_pem_compact ] } if root OmfCommon.init(:development, opts) do |el| setup_logging(global_options) OmfCommon.comm.on_connected do |comm| info "Connected using #{comm.conn_info}" info "Start experiment: #{OmfEc.experiment.id}" OmfCommon::Auth::CertificateStore.instance.register(root) if root OmfCommon::Auth::CertificateStore.instance.register(entity, OmfCommon.comm.local_address) if entity begin include OmfEc::Backward::DefaultEvents load exp_path rescue => e error e.message error e.backtrace.join("\n") end comm.on_interrupted { comm.disconnect } end end rescue => e logger.fatal e.message logger.fatal e.backtrace.join("\n") end end exit run(ARGV)