ruby-cute 0.12 → 0.24
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 +5 -5
- data/.gitlab-ci.yml +55 -0
- data/README.md +12 -1
- data/bin/cute +2 -1
- data/bin/grd +701 -0
- data/debian/.gitattributes +3 -0
- data/debian/changelog +108 -0
- data/debian/control +22 -5
- data/debian/ruby-cute.docs +1 -1
- data/debian/rules +3 -11
- data/debian/watch +2 -2
- data/examples/distem-bootstrap +16 -22
- data/examples/g5k-tutorial.md +25 -18
- data/examples/g5k_exp_virt.rb +1 -1
- data/lib/cute/bash.rb +7 -7
- data/lib/cute/configparser.rb +14 -12
- data/lib/cute/execute.rb +6 -4
- data/lib/cute/extensions.rb +3 -4
- data/lib/cute/g5k_api.rb +62 -38
- data/lib/cute/net-ssh-exec3.rb +10 -7
- data/lib/cute/net-ssh.rb +2 -2
- data/lib/cute/net.rb +5 -7
- data/lib/cute/synchronization.rb +1 -3
- data/lib/cute/taktuk.rb +4 -5
- data/lib/cute/version.rb +1 -1
- data/ruby-cute.gemspec +6 -3
- data/spec/g5k_api_check_spec.rb +1 -1
- data/spec/g5k_api_spec.rb +14 -22
- data/spec/spec_helper.rb +10 -6
- data/test/test_execute.rb +0 -0
- metadata +55 -11
- data/debian/compat +0 -1
    
        data/lib/cute/g5k_api.rb
    CHANGED
    
    | @@ -24,7 +24,7 @@ module Cute | |
| 24 24 | 
             
                # - {Cute::G5K::Unauthorized} it means that there is an authentication problem.
         | 
| 25 25 | 
             
                # - {Cute::G5K::EventTimeout} this exception is triggered by the methods that wait for events such as:
         | 
| 26 26 | 
             
                #   job submission and environment deployment.
         | 
| 27 | 
            -
                class Error <  | 
| 27 | 
            +
                class Error < StandardError
         | 
| 28 28 | 
             
                  attr_accessor :orig # Original exception
         | 
| 29 29 |  | 
| 30 30 | 
             
                  def initialize(message = nil, object = nil)
         | 
| @@ -63,6 +63,7 @@ module Cute | |
| 63 63 | 
             
                #
         | 
| 64 64 | 
             
                #    end
         | 
| 65 65 | 
             
                class BadRequest < Error
         | 
| 66 | 
            +
                  attr_accessor :inner_url, :inner_code, :inner_title, :inner_message
         | 
| 66 67 | 
             
                end
         | 
| 67 68 |  | 
| 68 69 | 
             
                # It wraps all Restclient exceptions with http codes: 403, 405,406, 412, 415, 500, 502, 503 and 504.
         | 
| @@ -205,7 +206,8 @@ module Cute | |
| 205 206 | 
             
                  # @param user [String] user if authentication is needed
         | 
| 206 207 | 
             
                  # @param pass [String] password if authentication is needed
         | 
| 207 208 | 
             
                  # @param on_error [Symbol] option to deactivate the {Cute::G5K::RequestFailed RequestFailed} exceptions
         | 
| 208 | 
            -
                   | 
| 209 | 
            +
                  # @param timeout [Integer] timeout for Rest request
         | 
| 210 | 
            +
                  def initialize(uri,api_version,user,pass,on_error,timeout)
         | 
| 209 211 | 
             
                    @user = user
         | 
| 210 212 | 
             
                    @pass = pass
         | 
| 211 213 | 
             
                    @api_version = api_version.nil? ? "stable" : api_version
         | 
| @@ -217,9 +219,8 @@ module Cute | |
| 217 219 | 
             
                      @endpoint = "https://#{user_escaped}:#{pass_escaped}@#{uri.split("https://")[1]}"
         | 
| 218 220 | 
             
                    end
         | 
| 219 221 |  | 
| 220 | 
            -
                    machine =`uname -ov`.chop
         | 
| 221 222 | 
             
                    @user_agent = "ruby-cute/#{VERSION} Ruby/#{RUBY_VERSION}"
         | 
| 222 | 
            -
                    @api = RestClient::Resource.new(@endpoint, :timeout =>  | 
| 223 | 
            +
                    @api = RestClient::Resource.new(@endpoint, :timeout => timeout,:verify_ssl => false)
         | 
| 223 224 | 
             
                    # some versions of restclient do not verify by default SSL certificates , :verify_ssl => true)
         | 
| 224 225 | 
             
                    # SSL verify is disabled due to Grid'5000 API certificate problem
         | 
| 225 226 | 
             
                    @on_error = on_error
         | 
| @@ -239,6 +240,12 @@ module Cute | |
| 239 240 | 
             
                  # @return [Hash] the HTTP response
         | 
| 240 241 | 
             
                  # @param path [String] this complements the URI to address to a specific resource
         | 
| 241 242 | 
             
                  def get_json(path)
         | 
| 243 | 
            +
                    return G5KJSON.parse(get_raw(path))
         | 
| 244 | 
            +
                  end
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                  # @return [String] the HTTP response
         | 
| 247 | 
            +
                  # @param path [String] this complements the URI to address to a specific resource
         | 
| 248 | 
            +
                  def get_raw(path)
         | 
| 242 249 | 
             
                    retries = 0
         | 
| 243 250 | 
             
                    begin
         | 
| 244 251 | 
             
                      r = resource(path).get(:content_type => "application/json",
         | 
| @@ -253,7 +260,7 @@ module Cute | |
| 253 260 | 
             
                      end
         | 
| 254 261 | 
             
                      handle_exception(e)
         | 
| 255 262 | 
             
                    end
         | 
| 256 | 
            -
                    return  | 
| 263 | 
            +
                    return r
         | 
| 257 264 | 
             
                  end
         | 
| 258 265 |  | 
| 259 266 | 
             
                  # Creates a resource on the server
         | 
| @@ -314,7 +321,7 @@ module Cute | |
| 314 321 | 
             
                  end
         | 
| 315 322 |  | 
| 316 323 | 
             
                  # Issues a Cute::G5K exception according to the http status code
         | 
| 317 | 
            -
                  def handle_exception(e,  | 
| 324 | 
            +
                  def handle_exception(e, _req = nil)
         | 
| 318 325 | 
             
                    puts("Error: #{$!}")
         | 
| 319 326 | 
             
                    puts("Backtrace:\n\t"+e.backtrace.join("\n\t"))
         | 
| 320 327 | 
             
                    if e.respond_to? :http_code
         | 
| @@ -332,11 +339,24 @@ module Cute | |
| 332 339 | 
             
                    case e.http_code
         | 
| 333 340 |  | 
| 334 341 | 
             
                    when  400
         | 
| 335 | 
            -
                       | 
| 342 | 
            +
                      br = BadRequest.new("Bad request (error 400): #{e.http_body}", e)
         | 
| 343 | 
            +
                      if e.http_body =~ /^Request to ([^ ]*) failed with status 400: (.*)$/m
         | 
| 344 | 
            +
                        br.inner_url = $1
         | 
| 345 | 
            +
                        json = $2
         | 
| 346 | 
            +
                        begin
         | 
| 347 | 
            +
                          d = JSON::parse(json)
         | 
| 348 | 
            +
                          br.inner_code = d['code']
         | 
| 349 | 
            +
                          br.inner_title = d['title']
         | 
| 350 | 
            +
                          br.inner_message = d['message']
         | 
| 351 | 
            +
                        rescue JSON::ParserError
         | 
| 352 | 
            +
                          # Ignore error
         | 
| 353 | 
            +
                        end
         | 
| 354 | 
            +
                      end
         | 
| 355 | 
            +
                      raise br
         | 
| 336 356 | 
             
                    when 404
         | 
| 337 | 
            -
                      raise NotFound.new("Resource not found", e)
         | 
| 357 | 
            +
                      raise NotFound.new("Resource not found (error 404): #{e.http_body}", e)
         | 
| 338 358 | 
             
                    when 401
         | 
| 339 | 
            -
                      raise Unauthorized.new("Authentication problem",e)
         | 
| 359 | 
            +
                      raise Unauthorized.new("Authentication problem (error 401): #{e.http_body}",e)
         | 
| 340 360 | 
             
                    else
         | 
| 341 361 | 
             
                      if @on_error == :ignore
         | 
| 342 362 | 
             
                        return nil
         | 
| @@ -549,6 +569,7 @@ module Cute | |
| 549 569 | 
             
                  # @option params [String] :password Password to access the REST API
         | 
| 550 570 | 
             
                  # @option params [Symbol] :on_error Set to :ignore if you want to ignore {Cute::G5K::RequestFailed ResquestFailed} exceptions.
         | 
| 551 571 | 
             
                  # @option params [Boolean] :debug Activate the debug mode
         | 
| 572 | 
            +
                  # @option params [Integer] :timeout Set the timeout in sec, default is 30 sec
         | 
| 552 573 | 
             
                  def initialize(params={})
         | 
| 553 574 | 
             
                    config = {}
         | 
| 554 575 | 
             
                    default_file = "#{ENV['HOME']}/.grid5000_api.yml"
         | 
| @@ -567,9 +588,10 @@ module Cute | |
| 567 588 | 
             
                    @api_version = params[:version] || config["version"] || "stable"
         | 
| 568 589 | 
             
                    @logger = nil
         | 
| 569 590 | 
             
                    @debug = params[:debug] || false
         | 
| 591 | 
            +
                    @timeout = params[:timeout] || 30
         | 
| 570 592 |  | 
| 571 593 | 
             
                    begin
         | 
| 572 | 
            -
                      @g5k_connection = G5KRest.new(@uri,@api_version,@user,@pass,params[:on_error])
         | 
| 594 | 
            +
                      @g5k_connection = G5KRest.new(@uri,@api_version,@user,@pass,params[:on_error], @timeout)
         | 
| 573 595 | 
             
                    rescue => e
         | 
| 574 596 |  | 
| 575 597 | 
             
                      msg_create_file = ""
         | 
| @@ -695,7 +717,9 @@ module Cute | |
| 695 717 | 
             
                  # @param site [String] a valid Grid'5000 site name
         | 
| 696 718 | 
             
                  # @param uid [String] user name in Grid'5000
         | 
| 697 719 | 
             
                  # @param states [Array] or [String] jobs state: running, waiting (multiple states can be specified)
         | 
| 698 | 
            -
                   | 
| 720 | 
            +
                  # @param details [String] pass "resources=yes to the query to get the list of resources
         | 
| 721 | 
            +
                  # @param recurse [String] after fetching the list of jobs, fetch details about each job individualy
         | 
| 722 | 
            +
                  def get_jobs(site, uid = nil, states = nil, details = false, recurse = true)
         | 
| 699 723 |  | 
| 700 724 | 
             
                    parameters = []
         | 
| 701 725 | 
             
                    if states then
         | 
| @@ -703,12 +727,18 @@ module Cute | |
| 703 727 | 
             
                      parameters.push("state=#{states.join(",")}")
         | 
| 704 728 | 
             
                    end
         | 
| 705 729 | 
             
                    parameters.push("user=#{uid}") if uid
         | 
| 706 | 
            -
                    parameters.push("limit= | 
| 730 | 
            +
                    parameters.push("limit=1000000")
         | 
| 731 | 
            +
                    parameters.push("resources=yes") if details
         | 
| 707 732 |  | 
| 733 | 
            +
                    info(debug_cmd(api_uri("/sites/#{site}/jobs?#{parameters.join("&")}"),"GET"), :debug)
         | 
| 708 734 | 
             
                    jobs = @g5k_connection.get_json(api_uri("/sites/#{site}/jobs?#{parameters.join("&")}")).items
         | 
| 709 | 
            -
                     | 
| 710 | 
            -
             | 
| 711 | 
            -
             | 
| 735 | 
            +
                    if recurse
         | 
| 736 | 
            +
                      jobs.map! do |j|
         | 
| 737 | 
            +
                        info(debug_cmd(j.rel_self, "GET"), :debug)
         | 
| 738 | 
            +
                        @g5k_connection.get_json(j.rel_self)
         | 
| 739 | 
            +
                      end
         | 
| 740 | 
            +
                    end
         | 
| 741 | 
            +
                    jobs.to_a
         | 
| 712 742 | 
             
                  end
         | 
| 713 743 |  | 
| 714 744 | 
             
                  # @return [Hash] the last 50 deployments performed in a Grid'5000 site
         | 
| @@ -970,6 +1000,7 @@ module Cute | |
| 970 1000 | 
             
                  # @option opts [String] :cmd The command to execute when the job starts (e.g. ./my-script.sh).
         | 
| 971 1001 | 
             
                  # @option opts [String] :cluster Valid Grid'5000 cluster
         | 
| 972 1002 | 
             
                  # @option opts [String] :queue A specific job queue
         | 
| 1003 | 
            +
                  # @option opts [String] :project A specific OAR project
         | 
| 973 1004 | 
             
                  # @option opts [Array]  :subnets 1) prefix_size, 2) number of subnets
         | 
| 974 1005 | 
             
                  # @option opts [String] :env Environment name for {http://kadeploy3.gforge.inria.fr/ Kadeploy}
         | 
| 975 1006 | 
             
                  # @option opts [String] :vlan VLAN type and number: kavlan-local, kavlan, kavlan-topo, etc
         | 
| @@ -984,12 +1015,12 @@ module Cute | |
| 984 1015 | 
             
                    # checking valid options
         | 
| 985 1016 | 
             
                    valid_opts = [:site, :cluster, :switches, :cpus, :cores, :nodes, :walltime, :cmd,
         | 
| 986 1017 | 
             
                                  :type, :name, :subnets, :env, :vlan, :num_vlan,:properties, :resources,
         | 
| 987 | 
            -
                                  :reservation, :wait, :keys, :queue, :env_user]
         | 
| 1018 | 
            +
                                  :reservation, :wait, :keys, :queue, :project, :env_user]
         | 
| 988 1019 | 
             
                    unre_opts = opts.keys - valid_opts
         | 
| 989 1020 | 
             
                    raise ArgumentError, "Unrecognized option #{unre_opts}" unless unre_opts.empty?
         | 
| 990 1021 |  | 
| 991 1022 | 
             
                    nodes = opts.fetch(:nodes, 1)
         | 
| 992 | 
            -
                    walltime = opts | 
| 1023 | 
            +
                    walltime = opts[:walltime]
         | 
| 993 1024 | 
             
                    site = opts[:site]
         | 
| 994 1025 | 
             
                    type = opts.fetch(:type, [])
         | 
| 995 1026 | 
             
                    name = opts.fetch(:name, 'rubyCute job')
         | 
| @@ -1007,6 +1038,7 @@ module Cute | |
| 1007 1038 | 
             
                    type = [type] if type.is_a?(Symbol)
         | 
| 1008 1039 | 
             
                    keys = opts[:keys]
         | 
| 1009 1040 | 
             
                    queue = opts[:queue]
         | 
| 1041 | 
            +
                    project = opts[:project]
         | 
| 1010 1042 | 
             
                    vlan = opts[:vlan]
         | 
| 1011 1043 | 
             
                    num_vlan = opts.fetch(:num_vlan, 1)
         | 
| 1012 1044 |  | 
| @@ -1021,14 +1053,11 @@ module Cute | |
| 1021 1053 | 
             
                      raise ArgumentError, "VLAN type not available in site #{site}" unless available_vlans.include?(vlan)
         | 
| 1022 1054 | 
             
                    end
         | 
| 1023 1055 |  | 
| 1024 | 
            -
                    raise 'At least nodes | 
| 1056 | 
            +
                    raise 'At least nodes and site must be given'  if [nodes, site].any? { |x| x.nil? }
         | 
| 1025 1057 |  | 
| 1026 | 
            -
                    raise 'nodes should be an integer or a string containing either ALL or BEST' unless (nodes.is_a?( | 
| 1058 | 
            +
                    raise 'nodes should be an integer or a string containing either ALL or BEST' unless (nodes.is_a?(Integer) or ["ALL","BEST"].include?(nodes))
         | 
| 1027 1059 |  | 
| 1028 | 
            -
                     | 
| 1029 | 
            -
                    walltime = walltime.to_time
         | 
| 1030 | 
            -
             | 
| 1031 | 
            -
                    command = "sleep #{secs}" if command.nil?
         | 
| 1060 | 
            +
                    command = "sleep infinity" if command.nil?
         | 
| 1032 1061 |  | 
| 1033 1062 | 
             
                    if resources == ""
         | 
| 1034 1063 | 
             
                      resources = "/switch=#{switches}" unless switches.nil?
         | 
| @@ -1037,14 +1066,16 @@ module Cute | |
| 1037 1066 | 
             
                      resources += "/core=#{cores}" unless cores.nil?
         | 
| 1038 1067 |  | 
| 1039 1068 | 
             
                      if cluster
         | 
| 1040 | 
            -
                        resources = (cluster.is_a?( | 
| 1069 | 
            +
                        resources = (cluster.is_a?(Integer) ? "/cluster=#{cluster}" : "{cluster='#{cluster}'}") + resources
         | 
| 1041 1070 | 
             
                      end
         | 
| 1042 1071 |  | 
| 1043 1072 | 
             
                      resources = "{type='#{vlan}'}/vlan=#{num_vlan}+" + resources unless vlan.nil?
         | 
| 1044 1073 | 
             
                      resources = "slash_#{subnets[0]}=#{subnets[1]}+" + resources unless subnets.nil?
         | 
| 1045 1074 | 
             
                    end
         | 
| 1046 1075 |  | 
| 1047 | 
            -
                     | 
| 1076 | 
            +
                    if walltime
         | 
| 1077 | 
            +
                      resources += ",walltime=#{walltime}" unless resources.include?("walltime")
         | 
| 1078 | 
            +
                    end
         | 
| 1048 1079 |  | 
| 1049 1080 | 
             
                    payload = {
         | 
| 1050 1081 | 
             
                               'resources' => resources,
         | 
| @@ -1052,12 +1083,13 @@ module Cute | |
| 1052 1083 | 
             
                               'command' => command
         | 
| 1053 1084 | 
             
                              }
         | 
| 1054 1085 |  | 
| 1055 | 
            -
                    info "Reserving resources: #{resources} ( | 
| 1086 | 
            +
                    info "Reserving resources: #{resources} (types: #{type.join(' ')}) (in #{site})"
         | 
| 1056 1087 |  | 
| 1057 1088 | 
             
                    payload['properties'] = properties unless properties.nil?
         | 
| 1058 1089 | 
             
                    payload['types'] = type.map{ |t| t.to_s} unless type.nil?
         | 
| 1059 1090 | 
             
                    type.map!{|t| t.to_sym}  unless type.nil?
         | 
| 1060 1091 | 
             
                    payload['queue'] = queue if queue
         | 
| 1092 | 
            +
                    payload['project'] = project if project
         | 
| 1061 1093 |  | 
| 1062 1094 | 
             
                    unless type.include?(:deploy)
         | 
| 1063 1095 | 
             
                      if opts[:keys]
         | 
| @@ -1067,18 +1099,10 @@ module Cute | |
| 1067 1099 |  | 
| 1068 1100 | 
             
                    if reservation
         | 
| 1069 1101 | 
             
                      payload['reservation'] = reservation
         | 
| 1070 | 
            -
                      info "Starting this reservation at #{reservation}"
         | 
| 1071 1102 | 
             
                    end
         | 
| 1072 1103 |  | 
| 1073 | 
            -
                     | 
| 1074 | 
            -
             | 
| 1075 | 
            -
                      r = @g5k_connection.post_json(api_uri("sites/#{site}/jobs"),payload)  # This makes reference to the same class
         | 
| 1076 | 
            -
                    rescue Error => e
         | 
| 1077 | 
            -
                      info "Fail to submit job"
         | 
| 1078 | 
            -
                      info e.message
         | 
| 1079 | 
            -
                      e.http_body.split("\\n").each{ |line| info line}
         | 
| 1080 | 
            -
                      raise
         | 
| 1081 | 
            -
                    end
         | 
| 1104 | 
            +
                    info debug_cmd(api_uri("sites/#{site}/jobs"),"POST",payload.to_json), :debug
         | 
| 1105 | 
            +
                    r = @g5k_connection.post_json(api_uri("sites/#{site}/jobs"),payload)  # This makes reference to the same class
         | 
| 1082 1106 |  | 
| 1083 1107 | 
             
                    job = @g5k_connection.get_json(r.rel_self)
         | 
| 1084 1108 | 
             
                    job = wait_for_job(job) if opts[:wait] == true
         | 
| @@ -1268,7 +1292,7 @@ module Cute | |
| 1268 1292 |  | 
| 1269 1293 | 
             
                    job["deploy"].map!{  |d| d.refresh(@g5k_connection) }
         | 
| 1270 1294 |  | 
| 1271 | 
            -
                    filter.keep_if{ | | 
| 1295 | 
            +
                    filter.keep_if{ |_k,v| v} # removes nil values
         | 
| 1272 1296 | 
             
                    if filter.empty?
         | 
| 1273 1297 | 
             
                      status = job["deploy"].map{ |d| d["status"] }
         | 
| 1274 1298 | 
             
                    else
         | 
| @@ -1346,7 +1370,7 @@ module Cute | |
| 1346 1370 | 
             
                  # @return [Array] machines that did not deploy successfully
         | 
| 1347 1371 | 
             
                  # @param deploy_info [Hash] deployment structure information
         | 
| 1348 1372 | 
             
                  def check_deployment(deploy_info)
         | 
| 1349 | 
            -
                    deploy_info["result"].select{ | | 
| 1373 | 
            +
                    deploy_info["result"].select{ |_p,v|  v["state"] == "KO"}.keys
         | 
| 1350 1374 | 
             
                  end
         | 
| 1351 1375 |  | 
| 1352 1376 | 
             
                  # Returns a valid  URI using the current G5K API version.
         | 
    
        data/lib/cute/net-ssh-exec3.rb
    CHANGED
    
    | @@ -14,17 +14,18 @@ class Net::SSH::Connection::Session | |
| 14 14 | 
             
                res[:stderr] = ""
         | 
| 15 15 | 
             
                res[:exit_code] = nil
         | 
| 16 16 | 
             
                res[:exit_signal] = nil
         | 
| 17 | 
            +
                ts = Time::now
         | 
| 17 18 | 
             
                open_channel do |channel|
         | 
| 18 | 
            -
                  channel.exec(command) do | | 
| 19 | 
            +
                  channel.exec(command) do |_ch, success|
         | 
| 19 20 | 
             
                    unless success
         | 
| 20 21 | 
             
                      abort "FAILED: couldn't execute command (ssh.channel.exec)"
         | 
| 21 22 | 
             
                    end
         | 
| 22 | 
            -
                    channel.on_data do | | 
| 23 | 
            +
                    channel.on_data do |_ch,data|
         | 
| 23 24 | 
             
                      print data unless o[:no_output]
         | 
| 24 25 | 
             
                      res[:stdout]+=data
         | 
| 25 26 | 
             
                    end
         | 
| 26 27 |  | 
| 27 | 
            -
                    channel.on_extended_data do | | 
| 28 | 
            +
                    channel.on_extended_data do |_ch,_type,data|
         | 
| 28 29 | 
             
                      print data unless o[:no_output]
         | 
| 29 30 | 
             
                      if o[:merge_outputs]
         | 
| 30 31 | 
             
                        res[:stdout]+=data
         | 
| @@ -33,14 +34,16 @@ class Net::SSH::Connection::Session | |
| 33 34 | 
             
                      end
         | 
| 34 35 | 
             
                    end
         | 
| 35 36 |  | 
| 36 | 
            -
                    channel.on_request("exit-status") do | | 
| 37 | 
            +
                    channel.on_request("exit-status") do |_ch,data|
         | 
| 37 38 | 
             
                      res[:exit_code] = data.read_long
         | 
| 38 | 
            -
                       | 
| 39 | 
            +
                      d = sprintf("%.1f", Time::now - ts)
         | 
| 40 | 
            +
                      puts "EXITCODE: #{res[:exit_code]} (duration: #{d}s)" unless o[:no_log]
         | 
| 39 41 | 
             
                    end
         | 
| 40 42 |  | 
| 41 | 
            -
                    channel.on_request("exit-signal") do | | 
| 43 | 
            +
                    channel.on_request("exit-signal") do |_ch, data|
         | 
| 42 44 | 
             
                      res[:exit_signal] = data.read_long
         | 
| 43 | 
            -
                       | 
| 45 | 
            +
                      d = sprintf("%.1f", Time::now - ts)
         | 
| 46 | 
            +
                      puts "EXITSIGNAL: #{res[:exit_signal]} (duration: #{d}s)" unless o[:no_log]
         | 
| 44 47 | 
             
                    end
         | 
| 45 48 | 
             
                  end
         | 
| 46 49 | 
             
                end
         | 
    
        data/lib/cute/net-ssh.rb
    CHANGED
    
    | @@ -115,7 +115,7 @@ module SessionActions | |
| 115 115 | 
             
                        Multi.logger.debug("[#{c.connection.host}] #{data.strip}")
         | 
| 116 116 | 
             
                      end
         | 
| 117 117 | 
             
                    end
         | 
| 118 | 
            -
                    channel.on_extended_data do |c,  | 
| 118 | 
            +
                    channel.on_extended_data do |c, _type, data|
         | 
| 119 119 | 
             
                      if block
         | 
| 120 120 | 
             
                        block.call(c, :stderr, data)
         | 
| 121 121 | 
             
                      else
         | 
| @@ -129,7 +129,7 @@ module SessionActions | |
| 129 129 | 
             
                      results[c.connection.host][:status] = c[:exit_status]
         | 
| 130 130 | 
             
                      if c[:exit_status] != 0
         | 
| 131 131 | 
             
                        Multi.logger.info("execution of '#{command}' on #{c.connection.host}
         | 
| 132 | 
            -
                                        failed with return status #{c[:exit_status] | 
| 132 | 
            +
                                        failed with return status #{c[:exit_status]}")
         | 
| 133 133 | 
             
                        if results[c.connection.host][:stdout]
         | 
| 134 134 | 
             
                          Multi.logger.info("--- stdout dump ---")
         | 
| 135 135 | 
             
                          Multi.logger.info(results[c.connection.host][:stdout])
         | 
    
        data/lib/cute/net.rb
    CHANGED
    
    | @@ -13,14 +13,12 @@ module Cute | |
| 13 13 | 
             
                end
         | 
| 14 14 |  | 
| 15 15 | 
             
                def Network::wait_open_port(host, port, timeout = 120)
         | 
| 16 | 
            -
                   | 
| 17 | 
            -
             | 
| 18 | 
            -
                   | 
| 19 | 
            -
             | 
| 20 | 
            -
                  while now() < bound do
         | 
| 21 | 
            -
                    t = now()
         | 
| 16 | 
            +
                  now = -> { return Time.now.to_f }
         | 
| 17 | 
            +
                  bound = now.call + timeout
         | 
| 18 | 
            +
                  while now.call < bound do
         | 
| 19 | 
            +
                    t = now.call
         | 
| 22 20 | 
             
                    return true if port_open?(host, port)
         | 
| 23 | 
            -
                    dt = now | 
| 21 | 
            +
                    dt = now.call - t
         | 
| 24 22 | 
             
                    sleep(0.5 - dt) if dt < 0.5
         | 
| 25 23 | 
             
                  end
         | 
| 26 24 | 
             
                  return false
         | 
    
        data/lib/cute/synchronization.rb
    CHANGED
    
    
    
        data/lib/cute/taktuk.rb
    CHANGED
    
    | @@ -123,7 +123,6 @@ module Cute | |
| 123 123 | 
             
                      yield  taktuk_cmd
         | 
| 124 124 | 
             
                      taktuk_cmd.loop unless taktuk_cmd.commands.empty?
         | 
| 125 125 | 
             
                      taktuk_cmd.free! if taktuk_cmd
         | 
| 126 | 
            -
                      taktuk_cmd = nil
         | 
| 127 126 | 
             
                    end
         | 
| 128 127 | 
             
                  else
         | 
| 129 128 | 
             
                    return taktuk_cmd
         | 
| @@ -372,9 +371,9 @@ module Cute | |
| 372 371 | 
             
                  def to_cmd
         | 
| 373 372 | 
             
                    self.inject([]) do |ret,val|
         | 
| 374 373 | 
             
                      if val =~ /^\[(.*)\]$/
         | 
| 375 | 
            -
                        ret  | 
| 374 | 
            +
                        ret + ['[',Regexp.last_match(1).strip,']']
         | 
| 376 375 | 
             
                      else
         | 
| 377 | 
            -
                        ret  | 
| 376 | 
            +
                        ret + val.split(' ')
         | 
| 378 377 | 
             
                      end
         | 
| 379 378 | 
             
                    end
         | 
| 380 379 | 
             
                  end
         | 
| @@ -417,7 +416,7 @@ module Cute | |
| 417 416 |  | 
| 418 417 | 
             
                    @streams.types.each{ |name|
         | 
| 419 418 | 
             
                      @args << '-o'
         | 
| 420 | 
            -
                      @args << "#{name | 
| 419 | 
            +
                      @args << "#{name}=#{@streams.to_cmd}"
         | 
| 421 420 | 
             
                    }
         | 
| 422 421 |  | 
| 423 422 | 
             
                    connector = build_connector
         | 
| @@ -563,7 +562,7 @@ module Cute | |
| 563 562 | 
             
                  #    tak.input(:file => "test_file.tar")
         | 
| 564 563 | 
             
                  def input(opts = {})
         | 
| 565 564 | 
             
                    mode = "broadcast"
         | 
| 566 | 
            -
                    @commands << "#{mode} input #{opts.keys.first | 
| 565 | 
            +
                    @commands << "#{mode} input #{opts.keys.first}"
         | 
| 567 566 | 
             
                    @commands << "[ #{opts.values.first} ]"
         | 
| 568 567 | 
             
                    @commands << ';'
         | 
| 569 568 | 
             
                  end
         | 
    
        data/lib/cute/version.rb
    CHANGED
    
    
    
        data/ruby-cute.gemspec
    CHANGED
    
    | @@ -5,9 +5,9 @@ require 'cute/version' | |
| 5 5 | 
             
            Gem::Specification.new do |s|
         | 
| 6 6 | 
             
              s.name        = "ruby-cute"
         | 
| 7 7 | 
             
              s.version     = Cute::VERSION
         | 
| 8 | 
            -
              s.authors     = ["Algorille  | 
| 9 | 
            -
              s.email       = " | 
| 10 | 
            -
              s.homepage    = "http://ruby-cute. | 
| 8 | 
            +
              s.authors     = ["Algorille/Madynes/RESIST teams at Inria/LORIA"]
         | 
| 9 | 
            +
              s.email       = "lucas.nussbaum@inria.fr"
         | 
| 10 | 
            +
              s.homepage    = "http://ruby-cute.github.io/"
         | 
| 11 11 | 
             
              s.summary     = "Critically Useful Tools for Experiments"
         | 
| 12 12 | 
             
              s.description = "Ruby library for controlling experiments"
         | 
| 13 13 | 
             
              s.required_rubygems_version = ">= 1.3.6"
         | 
| @@ -26,7 +26,10 @@ Gem::Specification.new do |s| | |
| 26 26 | 
             
              s.add_dependency 'rest-client', '>= 1.6'
         | 
| 27 27 | 
             
              s.add_dependency 'json', '>= 1.8'
         | 
| 28 28 | 
             
              s.add_dependency 'ipaddress', '>= 0.8'
         | 
| 29 | 
            +
              s.add_dependency 'net-ssh', '>= 3.2'
         | 
| 29 30 | 
             
              s.add_dependency 'net-ssh-multi', '>= 1.2'
         | 
| 31 | 
            +
              s.add_dependency 'net-scp', '>= 1.2'
         | 
| 32 | 
            +
              s.add_dependency 'peach', '>= 0.5.1'
         | 
| 30 33 |  | 
| 31 34 | 
             
              s.extra_rdoc_files = ['README.md', 'LICENSE']
         | 
| 32 35 | 
             
              s.license = 'CeCILL-B'
         | 
    
        data/spec/g5k_api_check_spec.rb
    CHANGED
    
    | @@ -4,7 +4,7 @@ require 'spec_helper' | |
| 4 4 |  | 
| 5 5 | 
             
            describe Cute::G5K::API do
         | 
| 6 6 |  | 
| 7 | 
            -
              subject {  | 
| 7 | 
            +
              subject { ENV['TEST_REAL'].nil?? Cute::G5K::API.new(:user => "test") : Cute::G5K::API.new() }
         | 
| 8 8 |  | 
| 9 9 | 
             
              before :each do
         | 
| 10 10 | 
             
                if ENV['TEST_REAL']
         | 
    
        data/spec/g5k_api_spec.rb
    CHANGED
    
    | @@ -4,9 +4,9 @@ require 'spec_helper' | |
| 4 4 | 
             
            describe Cute::G5K::API do
         | 
| 5 5 |  | 
| 6 6 | 
             
              if ENV['TEST_REAL']
         | 
| 7 | 
            -
                subject {  | 
| 7 | 
            +
                subject { ENV['DEBUG'].nil?? Cute::G5K::API.new() : Cute::G5K::API.new(:debug => true) }
         | 
| 8 8 | 
             
              else
         | 
| 9 | 
            -
                subject {  | 
| 9 | 
            +
                subject { ENV['DEBUG'].nil?? Cute::G5K::API.new(:user => "test") : Cute::G5K::API.new(:user => "test",:debug => true) }
         | 
| 10 10 | 
             
              end
         | 
| 11 11 |  | 
| 12 12 | 
             
              let(:sites) { subject.site_uids}
         | 
| @@ -69,27 +69,20 @@ describe Cute::G5K::API do | |
| 69 69 | 
             
                expect(subject.get_deployments(@rand_site)).to be_an_instance_of(Cute::G5K::G5KArray)
         | 
| 70 70 | 
             
              end
         | 
| 71 71 |  | 
| 72 | 
            +
            =begin
         | 
| 72 73 | 
             
              it "raises an authentication error" do
         | 
| 73 74 | 
             
                expect{Cute::G5K::API.new(:user => "fake", :pass => "fake") }.to raise_error
         | 
| 74 75 | 
             
              end
         | 
| 76 | 
            +
            =end
         | 
| 75 77 |  | 
| 76 | 
            -
              it "raises a  | 
| 77 | 
            -
                expect{subject.get_jobs(" | 
| 78 | 
            -
              end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
              it "raises a bad request error" do
         | 
| 81 | 
            -
                expect{ subject.reserve(:site => @rand_site, :resources =>"/slash_22=1+{nonsense}")}.to raise_error(Cute::G5K::BadRequest)
         | 
| 82 | 
            -
            #    expect{ subject.reserve(:site => @rand_site, :resources =>"{ib30g='YES'}/nodes=2")}.to raise_error(Cute::G5K::BadRequest)
         | 
| 83 | 
            -
              end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
              it "raises a bad request using OAR API" do
         | 
| 86 | 
            -
                expect{subject.reserve(:site => @rand_site, :resources =>"nodes=1",:keys => "~/jobkey_nonexisting")}.to raise_error(Cute::G5K::BadRequest)
         | 
| 87 | 
            -
              end
         | 
| 88 | 
            -
             | 
| 89 | 
            -
              it "raises an exception at deploying" do
         | 
| 90 | 
            -
                expect{ subject.reserve(:site => @rand_site, :nodes => 1, :env => "nonsense")}.to raise_error(Cute::G5K::RequestFailed)
         | 
| 78 | 
            +
              it "raises a not found error" do
         | 
| 79 | 
            +
                expect{subject.get_jobs("not-found")}.to raise_error(Cute::G5K::NotFound)
         | 
| 91 80 | 
             
              end
         | 
| 92 81 |  | 
| 82 | 
            +
            #   it "raises an exception at deploying" do
         | 
| 83 | 
            +
            #     expect{ subject.reserve(:site => @rand_site, :nodes => 1, :env => "nonsense")}.to raise_error(Cute::G5K::RequestFailed)
         | 
| 84 | 
            +
            #   end
         | 
| 85 | 
            +
            # 
         | 
| 93 86 | 
             
              it "raises argument errors" do
         | 
| 94 87 | 
             
                job = Cute::G5K::G5KJSON.new
         | 
| 95 88 | 
             
                expect {subject.deploy(:env => "env")}.to raise_error(ArgumentError)
         | 
| @@ -185,10 +178,10 @@ describe Cute::G5K::API do | |
| 185 178 | 
             
              end
         | 
| 186 179 |  | 
| 187 180 |  | 
| 188 | 
            -
              it "submits a job and then deploy" do
         | 
| 189 | 
            -
                expect(subject.reserve(:site => @rand_site, :env => @env)).to have_key("deploy")
         | 
| 190 | 
            -
              end
         | 
| 191 | 
            -
             | 
| 181 | 
            +
            #  it "submits a job and then deploy" do
         | 
| 182 | 
            +
            #    expect(subject.reserve(:site => @rand_site, :env => @env)).to have_key("deploy")
         | 
| 183 | 
            +
            #  end
         | 
| 184 | 
            +
            #
         | 
| 192 185 |  | 
| 193 186 | 
             
              it "returns the same information" do
         | 
| 194 187 | 
             
                #low level REST access
         | 
| @@ -197,7 +190,6 @@ describe Cute::G5K::API do | |
| 197 190 | 
             
              end
         | 
| 198 191 |  | 
| 199 192 | 
             
              it "submit and does not wait for the reservation" do
         | 
| 200 | 
            -
                cluster = subject.cluster_uids(@rand_site).first
         | 
| 201 193 | 
             
                job = subject.reserve(:site => @rand_site, :wait => false)
         | 
| 202 194 | 
             
                job = subject.wait_for_job(job, :wait_time => 600)
         | 
| 203 195 | 
             
                expect(job).to include('state' => "running")
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -30,6 +30,7 @@ class FakeG5KResponse < Hash | |
| 30 30 | 
             
                            'nodes' => {"node1" => {"hard"=> "alive", "soft"=>"busy"}}
         | 
| 31 31 | 
             
                           }
         | 
| 32 32 | 
             
              def initialize(num_items = 2)
         | 
| 33 | 
            +
                super
         | 
| 33 34 | 
             
                MEDIA_TYPE.each { |key,value| self[key] = value}
         | 
| 34 35 | 
             
                self['items'] = []
         | 
| 35 36 | 
             
                num_items.times.each{ self['items'].push(MEDIA_TYPE) }
         | 
| @@ -46,33 +47,36 @@ RSpec.configure do |config| | |
| 46 47 | 
             
              # uri_sites = Addressable::Template.new "https://{user}:{password}@api.grid5000.fr/{version}/sites"
         | 
| 47 48 | 
             
              config.before(:each) do
         | 
| 48 49 |  | 
| 50 | 
            +
                stub_request(:any,/^https:\/\/api.grid5000.fr\/.*/).
         | 
| 51 | 
            +
                  to_return(:status => 200, :body => g5k_media_type.to_json, :headers => {})
         | 
| 52 | 
            +
             | 
| 49 53 | 
             
                stub_request(:any,/^https:\/\/.*\:.*@api.grid5000.fr\/.*/).
         | 
| 50 54 | 
             
                  to_return(:status => 200, :body => g5k_media_type.to_json, :headers => {})
         | 
| 51 55 |  | 
| 52 56 | 
             
                stub_request(:any,/^https:\/\/fake:fake@api.grid5000.fr\.*/).
         | 
| 53 57 | 
             
                  to_return(:status => 401)
         | 
| 54 58 |  | 
| 55 | 
            -
                stub_request(:any,/^https | 
| 59 | 
            +
                stub_request(:any,/^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*\/sites\/not-found\/.*/).
         | 
| 56 60 | 
             
                  to_return(:status => 404)
         | 
| 57 61 |  | 
| 58 | 
            -
                stub_request(:any,/^https | 
| 62 | 
            +
                stub_request(:any,/^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*\/sites\/tmpfail\/.*/).
         | 
| 59 63 | 
             
                  to_return(:status => 503).
         | 
| 60 64 | 
             
                  to_return(:status => 200, :body => g5k_media_type.to_json, :headers => {})
         | 
| 61 65 |  | 
| 62 | 
            -
                stub_request(:get,/^https | 
| 66 | 
            +
                stub_request(:get,/^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*\/sites\/.*vlans$/).
         | 
| 63 67 | 
             
                  to_return(:status => 200, :body => {'total' => 3, 'items' => [{'type' => "kavlan-local"},{'type' => "kvlan"}]}.to_json)
         | 
| 64 68 |  | 
| 65 69 | 
             
                  # to_return(:status => 200, :body => {:total => 3, :items => [{:type => "kavlan-local"},{:type => "kavlan"}]})
         | 
| 66 70 |  | 
| 67 | 
            -
                stub_request(:post, /^https | 
| 71 | 
            +
                stub_request(:post, /^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*/).
         | 
| 68 72 | 
             
                  with(:body => hash_including("resources" => "/slash_22=1+{nonsense},walltime=01:00")).
         | 
| 69 73 | 
             
                  to_return(:status => 400, :body => "Oarsub failed: please verify your request syntax")
         | 
| 70 74 |  | 
| 71 | 
            -
                stub_request(:post, /^https | 
| 75 | 
            +
                stub_request(:post, /^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*/).
         | 
| 72 76 | 
             
                  with(:body => hash_including("import-job-key-from-file" => [ File.expand_path("~/jobkey_nonexisting") ])).
         | 
| 73 77 | 
             
                  to_return(:status => 400, :body => "Oarsub failed: please verify your request syntax")
         | 
| 74 78 |  | 
| 75 | 
            -
                stub_request(:post, /^https | 
| 79 | 
            +
                stub_request(:post, /^https:\/\/(.*\:.*@)?api.grid5000.fr\/.*/).
         | 
| 76 80 | 
             
                  with(:body => hash_including("environment" => "nonsense")).
         | 
| 77 81 | 
             
                  to_return(:status => 500, :body => "Invalid environment specification")
         | 
| 78 82 |  | 
    
        data/test/test_execute.rb
    CHANGED
    
    | 
            File without changes
         |