osc-reservations 1.0.3

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.
@@ -0,0 +1,39 @@
1
+ module OSC
2
+ module Reservations
3
+ # Batch server that utilizes reservations in the batch scheduler.
4
+ class Batch
5
+ # @return [String] the batch server to connect to.
6
+ attr_reader :server
7
+
8
+ # @param server [String] The server to connect to.
9
+ # @param context [Hash] An optional hash of values that may be required by chosen adapter.
10
+ def initialize(server, context = {})
11
+ @server = server
12
+
13
+ # symbolize keys
14
+ @context = {}
15
+ context.each do |key, value|
16
+ @context[key.to_sym] = value
17
+ end
18
+ end
19
+
20
+ # See if the method call exists as a key in @context.
21
+ #
22
+ # @param method_name the method name called
23
+ # @param arguments the arguments to the call
24
+ # @param block an optional block for the call
25
+ def method_missing(method_name, *arguments, &block)
26
+ @context.fetch(method_name) { super }
27
+ end
28
+
29
+ # Checks if the method responds to an instance method, or is able to
30
+ # proxy it to @context.
31
+ #
32
+ # @param method_name the method name to check
33
+ # @return [Boolean]
34
+ def respond_to_missing?(method_name, include_private = false)
35
+ @context.include?(method_name) || super
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ module OSC
2
+ module Reservations
3
+ # Provides a way for the developer to view the properties of any given
4
+ # reserved node.
5
+ class Node
6
+ # @return [String] The ID of this node.
7
+ attr_accessor :id
8
+
9
+ # @return [Fixnum] Number of cores on this node.
10
+ attr_accessor :np
11
+
12
+ # @return [Array<String>] A list of properties for this node.
13
+ attr_accessor :props
14
+
15
+ # @return [Array<String>] A list of job id's running on this node.
16
+ attr_accessor :jobs
17
+
18
+ # @return [Array<String>] A list of users running on this node.
19
+ attr_accessor :users
20
+
21
+ # @param opts [Hash] Options used to create a node.
22
+ # @option opts [String] :id The ID of this node.
23
+ # @option opts [Fixnum] :np The number of processors on this node.
24
+ # @option opts [Array<String>] :props An array of properties for this node.
25
+ # @option opts [Array<String>] :jobs An array of job IDs running on this node.
26
+ # @option opts [Array<String>] :users An array of users running on this node.
27
+ def initialize(opts)
28
+ @id = opts[:id]
29
+ @np = opts[:np]
30
+ @props = opts[:props]
31
+ @jobs = opts[:jobs]
32
+ @users = opts[:users]
33
+ end
34
+
35
+ # Is this node free to use?
36
+ # @return [Boolean] is node free?
37
+ def free?
38
+ jobs.empty?
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ require 'yaml'
2
+
3
+ module OSC
4
+ module Reservations
5
+ # Factory for creating reservation objects that detail the current user's
6
+ # reservations on the given batch server.
7
+ class Query
8
+ # See if the method call exists as a key in batch config yaml file.
9
+ # Examples:
10
+ # Query.glenn()
11
+ # Query.oakley()
12
+ #
13
+ # @param method_name the method name called
14
+ # @param arguments the arguments to the call
15
+ # @param block an optional block for the call
16
+ def self.method_missing(method_name, *arguments, &block)
17
+ context = Reservations.batch_config[method_name.to_s]
18
+ if context
19
+ adapter_type = context['adapter']['type']
20
+ batch_server = context['batch'].delete('server')
21
+ batch_context = context['batch']
22
+ a = Object.const_get(adapter_type).new
23
+ b = Batch.new(batch_server, batch_context)
24
+ new(a, b)
25
+ else
26
+ super
27
+ end
28
+ end
29
+
30
+ # Checks if the method responds to an instance method, or is able to
31
+ # proxy it to the batch config yaml file.
32
+ #
33
+ # @param method_name the method name to check
34
+ # @return [Boolean]
35
+ def self.respond_to_missing?(method_name, include_private = false)
36
+ Reservations.batch_config.include?(method_name.to_s) || super
37
+ end
38
+
39
+ # @param adapter [Adapter] The adapter used to communicate with the scheduler.
40
+ # @param batch [Batch] The batch server to communicate with.
41
+ def initialize(adapter, batch)
42
+ @adapter = adapter
43
+ @batch = batch
44
+ end
45
+
46
+ # Query the reservation information for a given reservation id.
47
+ # @param id [String] The reservation id to query for.
48
+ # @return [Reservation, nil] The reservation queried for.
49
+ def reservation(id)
50
+ @adapter.query_reservation(@batch, id)
51
+ end
52
+
53
+ # Query for all the reservations.
54
+ # @return [Array<Reservation>] A list of reservations for the user.
55
+ def reservations
56
+ @adapter.query_reservations(@batch)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,53 @@
1
+ module OSC
2
+ module Reservations
3
+ # Provides a way for developers to interact with a scheduler independent
4
+ # reservation.
5
+ class Reservation
6
+ # @return [String] The ID for this reservation.
7
+ attr_accessor :id
8
+
9
+ # @return [Time] The time when this reservation begins.
10
+ attr_accessor :starttime
11
+
12
+ # @return [Time] The time when this reservation ends.
13
+ attr_accessor :endtime
14
+
15
+ # @return [Array<Node>] The list of nodes reserved under this reservation.
16
+ attr_accessor :nodes
17
+
18
+ # @return [String] The accounting creds user (who gets charged for unused resources).
19
+ attr_accessor :auser
20
+
21
+ # @return [Array<String>] The list of users who have access to this reservation.
22
+ attr_accessor :users
23
+
24
+ # @param opts [Hash] The options to create a reservation with.
25
+ # @option opts [String] :id The ID of the reservation.
26
+ # @option opts [Time] :starttime The time the reservations begins.
27
+ # @option opts [Time] :endtime The time the reservation ends.
28
+ # @option opts [Array<Node>] :nodes An array of nodes allocated for the reservation.
29
+ # @option opts [String] :auser The accounting user to be charged for unused CPU of reservation.
30
+ # @option opts [Array<String>] :users An array of users who have access to this reservation.
31
+ def initialize(opts)
32
+ @id = opts[:id]
33
+ @starttime = opts[:starttime]
34
+ @endtime = opts[:endtime]
35
+ @nodes = opts[:nodes]
36
+ @auser = opts[:auser]
37
+ @users = opts[:users]
38
+ end
39
+
40
+ # List of nodes that are free to use for this reservation.
41
+ # @return [Array<Node>] The list of nodes free to use.
42
+ def free_nodes
43
+ nodes.select(&:free?)
44
+ end
45
+
46
+ # Has this reservation started yet?
47
+ # @return [Boolean] Whether this reservation has started yet.
48
+ def started?
49
+ Time.now >= starttime
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module OSC
2
+ module Reservations
3
+ # The current version of OSC::Reservations.
4
+ VERSION = "1.0.3"
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'osc/reservations/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "osc-reservations"
8
+ spec.version = OSC::Reservations::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ["Jeremy Nicklas"]
11
+ spec.email = ["jnicklas@osc.edu"]
12
+ spec.summary = %q{Query current OSC reservations for running user}
13
+ spec.description = %q{This library queries active OSC reservations for the running user.}
14
+ spec.homepage = "https://github.com/OSC/osc-reservations"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_runtime_dependency "nokogiri", "~> 1.6"
23
+ spec.add_runtime_dependency "pbs", "~> 1.0"
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
@@ -0,0 +1,115 @@
1
+ require 'minitest/autorun'
2
+ require 'osc/reservations'
3
+
4
+ require 'ostruct'
5
+ require 'yaml'
6
+
7
+ # Stub out command line methods
8
+ module Open3
9
+ def popen3(*cmd,&block)
10
+ line = cmd[0]
11
+
12
+ wait_thr = nil
13
+ if line =~ /NIL_RSV/
14
+ wait_thr = OpenStruct.new(value: OpenStruct.new(:'success?' => false))
15
+ else
16
+ wait_thr = OpenStruct.new(value: OpenStruct.new(:'success?' => true))
17
+ end
18
+
19
+ file = "osc_rsv.xml" if line =~ /mrsvctl/
20
+ file = "osc_rsvs.xml" if line =~ /mrsvctl/ && line =~ /ALL/
21
+ File.open("test/files/#{file}", "r") do |f|
22
+ yield "", f, "", wait_thr
23
+ end
24
+ end
25
+ module_function :popen3
26
+ end
27
+
28
+ # Stub out PBS methods
29
+ module PBS
30
+ class Conn
31
+ def self.batch(name)
32
+ name
33
+ end
34
+ end
35
+ class Query
36
+ def initialize(opts)
37
+ @conn = opts[:conn]
38
+ @type = opts[:type]
39
+ end
40
+ def find(opts)
41
+ YAML.load_file("test/files/osc_#{@conn}_#{@type}_#{opts[:id]}.yml")
42
+ end
43
+ end
44
+ end
45
+
46
+ class TestOSCMoab < Minitest::Test
47
+ def setup
48
+ @batch = OpenStruct.new(server: 'a.b.c', torque_name: 'oakley', mrsvctl: 'mrsvctl')
49
+ @adapter = OSC::Reservations::Adapters::OSCMoab.new
50
+ end
51
+
52
+ def test_osc_moab
53
+ assert_respond_to @adapter, :query_reservation
54
+ assert_respond_to @adapter, :query_reservations
55
+ assert_respond_to @adapter, :submit_reservation
56
+ end
57
+
58
+ def test_query_reservation
59
+ rsv = @adapter.query_reservation(@batch, 'NIL_RSV')
60
+ assert_equal nil, rsv
61
+
62
+ rsv = @adapter.query_reservation(@batch, 'wiag.1809')
63
+ test_wiag_rsv(rsv)
64
+ end
65
+
66
+ def test_query_reservations
67
+ rsv_list = @adapter.query_reservations(@batch)
68
+ assert_equal 2, rsv_list.length
69
+
70
+ rsv = rsv_list.first
71
+ test_wiag_rsv(rsv)
72
+
73
+ # reservation that hasn't started yet
74
+ rsv = rsv_list[1]
75
+ assert_equal 4, rsv.nodes.length
76
+ assert_operator Time.now, :<, rsv.starttime
77
+ end
78
+
79
+ def test_submit_reservation
80
+ @adapter.submit_reservation(@batch, nil)
81
+ end
82
+
83
+ private
84
+
85
+ def test_wiag_rsv(rsv)
86
+ assert_equal "wiag.1809", rsv.id
87
+ assert_equal Time.at(1433854480), rsv.starttime
88
+ assert_equal Time.at(1633854480), rsv.endtime
89
+ assert_equal "judithg", rsv.auser
90
+ assert_equal ["bmcmichael", "jnicklas", "awe0011", "ndem0009", "efranz", "ndem0010", "bgohar"], rsv.users
91
+
92
+ nodes = rsv.nodes
93
+ assert_equal 4, nodes.length
94
+
95
+ # node with no jobs
96
+ node = nodes[0]
97
+ assert_equal "n0613", node.id
98
+ assert_equal 12, node.np
99
+ assert_equal ["cbb15", "partdual", "udapl"], node.props
100
+ assert_equal [], node.jobs
101
+ assert_equal [], node.users
102
+
103
+ # node with one job using all cores
104
+ node = nodes[3]
105
+ assert_equal "n0609", node.id
106
+ assert_equal ["4691489.oak-batch.osc.edu"], node.jobs
107
+ assert_equal ["jnicklas"], node.users
108
+
109
+ # node with multiple jobs on it
110
+ node2 = nodes[2]
111
+ assert_equal "n0611", node2.id
112
+ assert_equal ["4691490.oak-batch.osc.edu", "4691491.oak-batch.osc.edu"], node2.jobs
113
+ assert_equal ["jnicklas"], node2.users
114
+ end
115
+ end
@@ -0,0 +1,4 @@
1
+ batch1:
2
+ adapter: 'FakeAdapter'
3
+ server: 'batch1.domain.com'
4
+ extra1: 'EXTRA1'
@@ -0,0 +1,47 @@
1
+ ---
2
+ - :name: 4691489.oak-batch.osc.edu
3
+ :attribs:
4
+ :Job_Name: TestJob
5
+ :Job_Owner: jnicklas@oakley01.osc.edu
6
+ :resources_used:
7
+ :cput: '00:00:00'
8
+ :energy_used: '0'
9
+ :mem: 2208kb
10
+ :vmem: 211332kb
11
+ :walltime: '00:00:14'
12
+ :job_state: R
13
+ :queue: serial
14
+ :server: oak-batch.osc.edu:15001
15
+ :Checkpoint: u
16
+ :ctime: '1443626884'
17
+ :Error_Path: oakley01.osc.edu:/nfs/17/jnicklas/ttt/TestJob.e4845720
18
+ :exec_host: n0675/0-11
19
+ :exec_port: '15003'
20
+ :Hold_Types: n
21
+ :Join_Path: n
22
+ :Keep_Files: n
23
+ :Mail_Points: a
24
+ :mtime: '1443626959'
25
+ :Output_Path: oakley01.osc.edu:/nfs/17/jnicklas/ttt/TestJob.o4845720
26
+ :Priority: '0'
27
+ :qtime: '1443626884'
28
+ :Rerunable: 'True'
29
+ :Resource_List:
30
+ :gattr: fs17
31
+ :nodect: '1'
32
+ :nodes: 1:ppn=12
33
+ :walltime: '03:00:00'
34
+ :session_id: '22136'
35
+ :Variable_List: PBS_O_QUEUE=batch,PBS_O_HOME=/nfs/17/jnicklas,PBS_O_LOGNAME=jnicklas,PBS_O_PATH=/nfs/gpfs/PZS0645/local-oakley/tree/1.7.0/bin:/nfs/gpfs/PZS0645/local-oakley/git/2.3.6/bin:/nfs/gpfs/PZS0645/local-oakley/vim/7.4/bin:/nfs/17/jnicklas/bin:/usr/lib64/qt-3.3/bin:/usr/local/mvapich2/intel/15/2.1/bin:/usr/local/intel/vtune_amplifier_xe_2015.3.0.403110/bin64:/usr/local/intel/inspector_xe_2015.1.2.379161/bin64:/usr/local/intel/composer_xe_2015.3.187/bin/intel64:/usr/local/moab/8.1.1.2-2015080516-eb28ad0-el6/bin:/usr/local/torque/5.1.1-1_fba25d92/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin,PBS_O_MAIL=/var/spool/mail/jnicklas,PBS_O_SHELL=/bin/bash,PBS_O_LANG=en_US.UTF-8,PBS_O_SUBMIT_FILTER=/usr/local/sbin/torque_submitfilter,PBS_O_WORKDIR=/nfs/17/jnicklas/ttt,PBS_O_HOST=oakley01.osc.edu,PBS_O_SERVER=oak-batch.osc.edu
36
+ :euser: jnicklas
37
+ :egroup: appl
38
+ :queue_type: E
39
+ :etime: '1443626884'
40
+ :submit_args: -l nodes=1:ppn=12 -l walltime=03:00:00 -N TestJob
41
+ :start_time: '1443626959'
42
+ :Walltime:
43
+ :Remaining: '10756'
44
+ :start_count: '1'
45
+ :fault_tolerant: 'False'
46
+ :job_radix: '0'
47
+ :submit_host: oakley01.osc.edu
@@ -0,0 +1,47 @@
1
+ ---
2
+ - :name: 4691489.oak-batch.osc.edu
3
+ :attribs:
4
+ :Job_Name: TestJob
5
+ :Job_Owner: jnicklas@oakley01.osc.edu
6
+ :resources_used:
7
+ :cput: '00:00:00'
8
+ :energy_used: '0'
9
+ :mem: 2208kb
10
+ :vmem: 211332kb
11
+ :walltime: '00:00:14'
12
+ :job_state: R
13
+ :queue: serial
14
+ :server: oak-batch.osc.edu:15001
15
+ :Checkpoint: u
16
+ :ctime: '1443626884'
17
+ :Error_Path: oakley01.osc.edu:/nfs/17/jnicklas/ttt/TestJob.e4845720
18
+ :exec_host: n0675/0-11
19
+ :exec_port: '15003'
20
+ :Hold_Types: n
21
+ :Join_Path: n
22
+ :Keep_Files: n
23
+ :Mail_Points: a
24
+ :mtime: '1443626959'
25
+ :Output_Path: oakley01.osc.edu:/nfs/17/jnicklas/ttt/TestJob.o4845720
26
+ :Priority: '0'
27
+ :qtime: '1443626884'
28
+ :Rerunable: 'True'
29
+ :Resource_List:
30
+ :gattr: fs17
31
+ :nodect: '1'
32
+ :nodes: 1:ppn=12
33
+ :walltime: '03:00:00'
34
+ :session_id: '22136'
35
+ :Variable_List: PBS_O_QUEUE=batch,PBS_O_HOME=/nfs/17/jnicklas,PBS_O_LOGNAME=jnicklas,PBS_O_PATH=/nfs/gpfs/PZS0645/local-oakley/tree/1.7.0/bin:/nfs/gpfs/PZS0645/local-oakley/git/2.3.6/bin:/nfs/gpfs/PZS0645/local-oakley/vim/7.4/bin:/nfs/17/jnicklas/bin:/usr/lib64/qt-3.3/bin:/usr/local/mvapich2/intel/15/2.1/bin:/usr/local/intel/vtune_amplifier_xe_2015.3.0.403110/bin64:/usr/local/intel/inspector_xe_2015.1.2.379161/bin64:/usr/local/intel/composer_xe_2015.3.187/bin/intel64:/usr/local/moab/8.1.1.2-2015080516-eb28ad0-el6/bin:/usr/local/torque/5.1.1-1_fba25d92/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin,PBS_O_MAIL=/var/spool/mail/jnicklas,PBS_O_SHELL=/bin/bash,PBS_O_LANG=en_US.UTF-8,PBS_O_SUBMIT_FILTER=/usr/local/sbin/torque_submitfilter,PBS_O_WORKDIR=/nfs/17/jnicklas/ttt,PBS_O_HOST=oakley01.osc.edu,PBS_O_SERVER=oak-batch.osc.edu
36
+ :euser: jnicklas
37
+ :egroup: appl
38
+ :queue_type: E
39
+ :etime: '1443626884'
40
+ :submit_args: -l nodes=1:ppn=12 -l walltime=03:00:00 -N TestJob
41
+ :start_time: '1443626959'
42
+ :Walltime:
43
+ :Remaining: '10756'
44
+ :start_count: '1'
45
+ :fault_tolerant: 'False'
46
+ :job_radix: '0'
47
+ :submit_host: oakley01.osc.edu