#!/usr/bin/env ruby require 'rubygems' require 'time' require 'omf_common' require 'optparse' @config = YAML.load_file('/etc/nitos_testbed_rc/omf_script_conf.yaml') # @config = YAML.load_file(File.join(File.dirname(File.expand_path(__FILE__)), '.../etc/omf_script_conf.yaml')) @auth = @config[:auth] @xmpp = @config[:xmpp] @omf = @config[:omf_script] options = {} opt_parser = OptionParser.new do |opts| opts.banner = "Usage: omf6 [COMMAND] [OPTIONS]" opts.separator "" opts.separator "Commands" opts.separator " load : load an image to a group of nodes" opts.separator " save : reate an image from a node" opts.separator " tell : change the status of a group of nodes (status can be on/off/reset)" opts.separator " stat : report the status of a group of nodes" opts.separator "Options" opts.on('-i', '--image IMAGE_NAME', 'the image name for load command (if nill default image will be loaded)') { |v| options[:image] = v } opts.on('-n', '--node NODE', 'node name for save command') { |v| options[:node] = v } opts.on('-a', '--status STATUS', 'the status you want to get the node to. Required only on tell command.') { |v| options[:status] = v } opts.on('-t', '--target_nodes NODES', 'target nodes you want to run the command too. Required on commands load, tell and stat.') {|v| options[:target_nodes] = v.split(",") } options[:last_action] = @omf[:last_action] #actions are reset and shutdown opts.on('-l', '--last_action ACTION', "action you want to perform after the commands is executed. Optional on commands load and save (last_action can be reset/shutdown. Default #{options[:last_action]})."){|v| options[:last_action] = v} options[:wait] = false opts.on('-w', '--wait', 'wait until pingable. Used in tell command (default vallue is off)'){|v| options[:wait] = true} end entity_cert = File.expand_path(@auth[:entity_cert]) entity_key = File.expand_path(@auth[:entity_key]) entity = OmfCommon::Auth::Certificate.create_from_pem(File.read(entity_cert))#, File.read(entity_key)) trusted_roots = File.expand_path(@auth[:root_cert_dir]) #run frisbee server def create_frisbeed(comm, fcontroller, port, options) fcontroller.create(:frisbeed, hrn: 'frisbee server', image: options[:image], port: port) do |reply_msg| #image "/var/lib/omf-images-5.4/baseline.ndz" if reply_msg.success? server = reply_msg.resource @@server = server server.on_subscribed do server.on_message do |m| if m.operation == :inform if m.read_content("event") == "STDOUT" puts m.read_content("msg") elsif m.read_content("event") == "EXIT" puts m.read_content("msg") end end end end else error ">>> Frisbeed resource creation failed - #{reply_msg[:reason]}" end end end #run frisbee client on a node def create_frisbee(comm, fcontroller, node, port, options) fcontroller.create(:frisbee, hrn: 'frisbee client', port: port, node_topic: "#{node.to_s}") do |reply_msg| if reply_msg.success? client = reply_msg.resource client.on_subscribed do client.on_message do |client_msg| if client_msg.operation == :inform if client_msg.read_property("event") == "STARTED"#only happens when frisbee runs with :application rc # @nof_nodes_up_frisbee += 1 # if @nof_nodes == @nof_nodes_up_frisbee # create_frisbeed(fcontroller, port) #frisbee server # puts "load operation started." # end elsif client_msg.read_property("event") == "STDOUT" puts "#{client_msg.read_property("node")}: #{client_msg.read_property("msg")}" elsif client_msg.read_property("event") == "EXIT" puts "#{client_msg.read_property("node")}: #{client_msg.read_property("msg")}" comm.subscribe('cm_factory') do |controller| controller.on_message do |cm_msg| if cm_msg.operation == :inform case cm_msg.read_content("itype") when 'STATUS' unless cm_msg.read_property("current").nil? || cm_msg.read_property("desired").nil? if cm_msg.read_property("current").to_sym == :pxe_off && cm_msg.read_property("desired").to_sym == :pxe_off n = cm_msg.read_property("node") @nodes_up_without_pxe << n if @nodes_up_pxe.length == (@nodes_up_without_pxe.length + @nodes_failed_without_pxe.length) puts "load proccess completed." fcontroller.release(@@server) do |reply_msg| comm.disconnect end end end end when 'ERROR' case cm_msg.read_property("event_type") when "AUTH" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" n = cm_msg.read_property("node_name") @nodes_failed_without_pxe << n if (@nodes_up_without_pxe.length + @nodes_failed_without_pxe.length) == options[:target_nodes].length puts "load proccess completed." fcontroller.release(@@server) do |reply_msg| comm.disconnect end end when "HTTP" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" n = cm_msg.read_property("node_name") if @nodes_retrying_without_pxe.include?(n) @nodes_retrying_without_pxe.delete(n) @nodes_failed_without_pxe << n if (@nodes_up_without_pxe.length + @nodes_failed_without_pxe.length) == options[:target_nodes].length fcontroller.release(@@server) do |reply_msg| comm.disconnect end end else @nodes_retrying_without_pxe << n controller.configure(state: {node: n.to_sym, status: :start_on_pxe}) end when "TIME_OUT" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" n = cm_msg.read_property("node_name") @nodes_failed_without_pxe << n if (@nodes_up_without_pxe.length + @nodes_failed_without_pxe.length) == options[:target_nodes].length fcontroller.release(@@server) do |reply_msg| comm.disconnect end end else error cm_msg.read_content('reason') if cm_msg.read_content("reason") end when 'WARN' warn cm_msg.read_content('reason') if cm_msg.read_content("reason") end end end puts "#{options[:last_action]} node '#{client_msg.read_property("node")}' out of PXE." controller.configure(state: {node: node.to_sym, status: :start_without_pxe, last_action: options[:last_action]}) end end end end end else error ">>> Frisbee resource creation failed - #{reply_msg[:reason]}" end end end def load(comm, options) comm.subscribe('cm_factory') do |controller| #TODO handle the case some nodes are not up and running unless controller.error? @nof_nodes = options[:target_nodes].length @nodes_up_without_pxe = [] @nodes_failed_without_pxe = [] @nodes_retrying_without_pxe = [] @nodes_up_pxe = [] nodes_failed_pxe = [] nodes_retrying_pxe = [] port = nil controller.on_message do |m| if m.operation == :inform case m.read_content("itype") when 'STATUS' unless m.read_property("current").nil? && m.read_property("desired").nil? if m.read_property("current").to_sym == :pxe_on && m.read_property("desired").to_sym == :pxe_on n = m.read_property("node_name") @nodes_up_pxe << n if @nof_nodes == (@nodes_up_pxe.length + nodes_failed_pxe.length) #all nodes are up and running on pxe puts "all nodes are up and running on pxe." comm.subscribe('frisbee_factory') do |fcontroller| puts "requesting available port" fcontroller.request([:ports]) do |m| port = m.read_property("ports") puts "running frisbee server on port '#{port}'" create_frisbeed(comm, fcontroller, port, options) @nodes_up_pxe.each do |node| create_frisbee(comm, fcontroller, node, port, options) #frisbee client end puts "running frisbee client on specified nodes." end end end else error "exit code: #{m.read_content('exit_code')}" if m.read_content('exit_code') end end when 'ERROR' case m.read_property("event_type") when "AUTH" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed_pxe << n if @nof_nodes == (@nodes_up_pxe.length + nodes_failed_pxe.length) #all nodes are up and running on pxe puts "all nodes are up and running on pxe." comm.subscribe('frisbee_factory') do |fcontroller| puts "requesting available port" fcontroller.request([:ports]) do |m| port = m.read_property("ports") puts "running frisbee server on port '#{port}'" create_frisbeed(comm, fcontroller, port, options) @nodes_up_pxe.each do |node| create_frisbee(comm, fcontroller, node, port, options) #frisbee client end puts "running frisbee client on specified nodes." end end end when "HTTP" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") if nodes_retrying_pxe.include?(n) nodes_retrying_pxe.delete(n) nodes_failed_pxe << n if @nof_nodes == (@nodes_up_pxe.length + nodes_failed_pxe.length) #all nodes are up and running on pxe puts "all nodes are up and running on pxe." comm.subscribe('frisbee_factory') do |fcontroller| puts "requesting available port" fcontroller.request([:ports]) do |m| port = m.read_property("ports") puts "running frisbee server on port '#{port}'" create_frisbeed(comm, fcontroller, port, options) @nodes_up_pxe.each do |node| create_frisbee(comm, fcontroller, node, port, options) #frisbee client end puts "running frisbee client on specified nodes." end end end else nodes_retrying_pxe << n controller.configure(state: {node: n.to_sym, status: :start_on_pxe}) end when "TIME_OUT" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed_pxe << n if @nof_nodes == (@nodes_up_pxe.length + nodes_failed_pxe.length) #all nodes are up and running on pxe puts "all nodes are up and running on pxe." comm.subscribe('frisbee_factory') do |fcontroller| puts "requesting available port" fcontroller.request([:ports]) do |m| port = m.read_property("ports") puts "running frisbee server on port '#{port}'" create_frisbeed(comm, fcontroller, port, options) @nodes_up_pxe.each do |node| create_frisbee(comm, fcontroller, node, port, options) #frisbee client end puts "running frisbee client on specified nodes." end end end else error m.read_content('reason') if m.read_content("reason") end when 'WARN' warn cm_msg.read_content('reason') if m.read_content("reason") end end end options[:target_nodes].each do |node| controller.configure(state: {node: node.to_sym, status: :start_on_pxe}) end puts "waiting for target nodes to boot on PXE." else error controller.inspect end end end #run imagezip server def create_imagezip_server(comm, fcontroller, port, options) fcontroller.create(:imagezip_server, {hrn: 'imagezip server', image: options[:image], port: port}) do |reply_msg| if reply_msg.success? server = reply_msg.resource @@server = server server.on_subscribed do server.on_message do |m| if m.operation == :inform if m.read_content("event") == "STDOUT" puts m.read_content("msg") elsif m.read_content("event") == "EXIT" puts m.read_content("msg") end end end end else error ">>> Imagezip Server resource creation failed - #{reply_msg[:reason]}" end end end #run imagezip client on a node def create_imagezip_client(comm, fcontroller, node, port, options) fcontroller.create(:imagezip_client, {hrn: 'imagezip client', port: port, node_topic: "#{node.to_s}"}) do |reply_msg| if reply_msg.success? client = reply_msg.resource client.on_subscribed do client.on_message do |client_msg| if client_msg.operation == :inform if client_msg.read_property("event") == "STDOUT" print "#{client_msg.read_property("msg")}" elsif client_msg.read_property("event") == "EXIT" puts "#{client_msg.read_property("node")}: #{client_msg.read_property("msg")}" comm.subscribe('cm_factory') do |controller| controller.on_message do |cm_msg| if cm_msg.operation == :inform case cm_msg.read_content("itype") when 'STATUS' unless cm_msg.read_property("current").nil? && cm_msg.read_property("desired").nil? if cm_msg.read_property("current").to_sym == :pxe_off && cm_msg.read_property("desired").to_sym == :pxe_off puts "save proccess completed. Image was saved in directory '/var/lib/omf-images-6'" fcontroller.release(@@server) do |reply_msg| comm.disconnect end end end when 'ERROR' case cm_msg.read_property("event_type") when "AUTH" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" fcontroller.release(@@server) do |reply_msg| comm.disconnect end when "HTTP" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" if @retried fcontroller.release(@@server) do |reply_msg| comm.disconnect end else @retried = true controller.configure(state: {node: node.to_sym, status: :start_without_pxe, last_action: options[:last_action]}) end when "TIME_OUT" logger.info "#{cm_msg.read_property("msg")}, exit code: #{cm_msg.read_property("exit_code")}" fcontroller.release(@@server) do |reply_msg| comm.disconnect end else error cm_msg.read_content('reason') if cm_msg.read_content("reason") end when 'WARN' warn cm_msg.read_content('reason') if cm_msg.read_content("reason") end end end puts "#{options[:last_action]} node '#{client_msg.read_property("node")}' out of PXE." controller.configure(state: {node: node.to_sym, status: :start_without_pxe, last_action: options[:last_action]}) end end end end end else error ">>> Frisbee resource creation failed - #{reply_msg[:reason]}" end end end def save(comm, options) comm.subscribe('cm_factory') do |controller| @retried = false unless controller.error? port = nil controller.on_message do |m| if m.operation == :inform puts m.inspect case m.read_content("itype") when 'STATUS' unless m.read_property("current").nil? && m.read_property("desired").nil? logger.info "#{m.read_property("node_name")}, current: #{m.read_property("current")}, desired: #{m.read_property("desired")}" if m.read_property("current").to_sym == :pxe_on && m.read_property("desired").to_sym == :pxe_on puts "node is up and running on pxe." comm.subscribe('frisbee_factory') do |fcontroller| puts "requesting available port" fcontroller.request([:ports]) do |m| port = m.read_property("ports") puts "running imagezip server on port #{port}." create_imagezip_server(comm, fcontroller, port, options) sleep 2 puts "running imagezip client on node." create_imagezip_client(comm, fcontroller, options[:node], port, options) end end else error "exit code: #{m.read_content('exit_code')}" if m.read_content('exit_code') end end when 'ERROR' case m.read_property("event_type") when "AUTH" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" comm.disconnect when "HTTP" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" if @retried comm.disconnect else @retried = true controller.configure(state: {node: options[:node].to_sym, status: :start_on_pxe}) end when "TIME_OUT" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" comm.disconnect else error m.read_content('reason') if m.read_content("reason") end when 'WARN' warn m.read_content('reason') if m.read_content("reason") end end end controller.configure(state: {node: options[:node].to_sym, status: :start_on_pxe}) puts "waiting for node to boot on PXE." else error controller.inspect end end end def tell(comm, options) comm.subscribe('cm_factory') do |controller| unless controller.error? nodes_failed = [] nodes_ok = [] nodes_retrying = [] controller.on_message do |m| if m.operation == :inform case m.read_content("itype") when 'STATUS' unless m.read_property("current").nil? && m.read_property("desired").nil? if m.read_property("current") != m.read_property("desired") if options[:wait] puts "Waiting for node '#{m.read_property("node_name")}'." logger.info "#{m.read_property("node_name")} is #{m.read_property("current")}." else n = m.read_property("node_name") nodes_ok << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length puts "all nodes are running." #TODO messages for failed nodes comm.disconnect end end else n = m.read_property("node_name") nodes_ok << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length puts "all nodes are #{m.read_property("desired")}." #TODO messages for failed nodes comm.disconnect end end end when 'ERROR' case m.read_property("event_type") when "AUTH" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end when "HTTP" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") if nodes_retrying.include?(n) nodes_retrying.delete(n) nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end else nodes_retrying << n info "retrying to send message #{options[:status]} to node #{n}" controller.configure(state: {node: n.to_sym, status: options[:status].to_sym}) end when "TIME_OUT" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end else error m.read_content('reason') if m.read_content("reason") comm.disconnect end when 'WARN' warn m.read_content('reason') if m.read_content("reason") end end end if options[:status] == "on" options[:target_nodes].each do |node| controller.configure(state: {node: node.to_sym, status: :on, wait: options[:wait]}) end elsif options[:status] == "off" options[:target_nodes].each do |node| controller.configure(state: {node: node.to_sym, status: :off, wait: options[:wait]}) end elsif options[:status] == "reset" options[:target_nodes].each do |node| controller.configure(state: {node: node.to_sym, status: :reset, wait: options[:wait]}) end else puts "Invalid value for -a, only on/off/reset values are available." puts opt_parser comm.disconnect end unless options[:wait] puts "Proccess complete." comm.disconnect end else error controller.inspect end end end def stat(comm, options) comm.subscribe('cm_factory') do |controller| unless controller.error? nodes_failed = [] nodes_ok = [] nodes_retrying = [] controller.on_message do |m| if m.operation == :inform case m.read_content("itype") when 'STATUS' unless m.read_property("current").nil? puts "#{m.read_property("node_name")} is #{m.read_property("current")}" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_ok << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end # elsif m.read_property("event_type") == "EXIT" # puts "ERROR: #{m.read_property("node")} - #{m.read_property("msg")}" # logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" # nof_nodes += 1 # if nof_nodes == options[:target_nodes].length # comm.disconnect # end end when 'ERROR' case m.read_property("event_type") when "AUTH" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end when "HTTP" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") if nodes_retrying.include?(n) nodes_retrying.delete(n) nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end else nodes_retrying << n info "retrying to send message #{options[:status]} to node #{n}" controller.configure(state: {node: n.to_sym, status: :get_status}) end when "TIME_OUT" logger.info "#{m.read_property("msg")}, exit code: #{m.read_property("exit_code")}" n = m.read_property("node_name") nodes_failed << n if (nodes_ok.length + nodes_failed.length) == options[:target_nodes].length comm.disconnect end else error m.read_content('reason') if m.read_content("itype") == 'ERROR' comm.disconnect end when 'WARN' warn m.read_content('reason') if m.read_content("itype") == 'WARN' end end end options[:target_nodes].each do |node| controller.configure(state: {node: node.to_sym, status: :get_status}) end else error controller.inspect comm.disconnect end end end opt_parser.parse! OmfCommon.init(@config[:operationMode], communication: { url: "xmpp://#{@xmpp[:script_user]}:#{@xmpp[:password]}@#{@xmpp[:server]}", auth: {} }) do OmfCommon.comm.on_connected do |comm| OmfCommon::Auth::CertificateStore.instance.register_default_certs(trusted_roots) entity.resource_id = OmfCommon.comm.local_topic.address OmfCommon::Auth::CertificateStore.instance.register(entity) info "Test script >> Connected to XMPP" case ARGV[0] when "load" if options[:node].nil? && options[:status].nil? && !options[:target_nodes].nil? if options[:last_action] == "reset" || options[:last_action] == "shutdown" puts "call load on options #{options.inspect}" load(comm, options) else puts "Invalid value for -l, only reset/shutdown values are available." puts opt_parser comm.disconnect end else puts "Invalid arguements." puts opt_parser comm.disconnect end when "save" if options[:image].nil? && !options[:node].nil? && options[:status].nil? && options[:target_nodes].nil? if options[:last_action] == "reset" || options[:last_action] == "shutdown" puts "call save on options #{options.inspect}" save(comm, options) else puts "Invalid value for -l, only reset/shutdown values are available." puts opt_parser comm.disconnect end else puts "Invalid arguements." puts opt_parser comm.disconnect end when "tell" if options[:image].nil? && options[:node].nil? && !options[:status].nil? && !options[:target_nodes].nil? if options[:status] == "on" || options[:status] == "off" || options[:status] == "reset" puts "call tell on options #{options.inspect}" tell(comm, options) else puts "Invalid value for -a, only on/off/reset values are available." puts opt_parser comm.disconnect end else puts "Invalid arguements." puts opt_parser comm.disconnect end when "stat" if options[:image].nil? && options[:node].nil? && options[:status].nil? && !options[:target_nodes].nil? puts "call stat on options #{options.inspect}" stat(comm, options) else puts "Invalid arguements." puts opt_parser comm.disconnect end else puts "Invalid command / options." puts opt_parser comm.disconnect end comm.on_interrupted { comm.disconnect } end end