Completed
Push — develop ( 5a28d4...622986 )
by
unknown
16s
created

detect_scheduling_sys()   D

Complexity

Conditions 8

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 8
c 4
b 1
f 0
dl 0
loc 34
rs 4
1
import sys
2
import os
3
from subprocess import Popen, PIPE, check_output
0 ignored issues
show
Unused Code introduced by
Unused PIPE imported from subprocess
Loading history...
Unused Code introduced by
Unused Popen imported from subprocess
Loading history...
4
import time
5
import uuid
6
from fabric.api import env, run, cd, get, hide, settings, remote_tunnel, show
0 ignored issues
show
Configuration introduced by
The import fabric.api could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
Unused Code introduced by
Unused remote_tunnel imported from fabric.api
Loading history...
Unused Code introduced by
Unused cd imported from fabric.api
Loading history...
Unused Code introduced by
Unused show imported from fabric.api
Loading history...
7
from fabric.tasks import execute
0 ignored issues
show
Configuration introduced by
The import fabric.tasks could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
8
from fabric.decorators import with_settings
0 ignored issues
show
Configuration introduced by
The import fabric.decorators could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
9
from datetime import timedelta
10
from os.path import join as pj
11
12
JOB_SCHEDULERS = ('SGE', 'SLURM', 'LSF',
13
                  'PBS', 'TORQUE', 'MAUI', 'LOADLEVELER')
14
15
scheduler = None
16
job_db = None
17
18
19
def get_data(filename):
20
    packagedir = os.path.dirname(__file__)
21
    dirname = pj(packagedir, '..', 'share', 'MyCluster')
22
    fullname = os.path.join(dirname, filename)
23
    # Need to check if file exists as
24
    # share location may also be sys.prefix/share
25
    if not os.path.isfile(fullname):
26
        dirname = pj(sys.prefix, 'share', 'MyCluster')
27
        fullname = os.path.join(dirname, filename)
28
29
    return fullname
30
31
32
def detect_scheduling_sys():
33
34
    # Test for SLURM
35
    if os.getenv('SLURMHOME') is not None:
36
        return my_import('mycluster.slurm')
37
38
    try:
39
        line = check_output(['scontrol', 'ping'])
40
        if line.split('(')[0] == 'Slurmctld':
41
            return my_import('mycluster.slurm')
42
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
43
        pass
44
45
46
    # Test for PBS
47
    try:
48
        line = check_output(['pbsnodes', '-a'])
49
        return my_import('mycluster.pbs')
50
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
51
        pass
52
53
    # Test for SGE
54
    if os.getenv('SGE_CLUSTER_NAME') is not None:
55
        return my_import('mycluster.sge')
56
57
    # Test for lsf
58
    try:
59
        line = check_output('lsid')
60
        if line.split(' ')[0] == 'Platform':
61
            return my_import('mycluster.lsf')
62
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
63
        pass
64
65
    return None
66
67
68
def queues():
69
    if scheduler is not None:
70
        return scheduler.queues()
71
    else:
72
        return []
73
74
75
def remote_sites():
76
    if job_db is not None:
77
        return job_db.remote_site_db
78
    else:
79
        return []
80
81
82
@with_settings(warn_only=True)
83
def remote_cmd():
84
    output_file = '~/.mycluster/' + str(uuid.uuid4())
85
    with hide('output', 'running', 'warnings'), settings(warn_only=True):
86
        run('mycluster -p >' + output_file, pty=False)
87
        import StringIO
0 ignored issues
show
Configuration introduced by
The import StringIO could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
88
        contents = StringIO.StringIO()
89
        get(output_file, contents)
90
        # operate on 'contents' like a file object here, e.g. 'print
91
        return contents.getvalue()
92
93
94
def remote_job_list(site):
95
    env.use_ssh_config = True
96
    return execute(remote_cmd, hosts=[site])
97
98
99
def print_timedelta(td):
100
    if (td.days > 0):
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after if.
Loading history...
101
        if td.days > 1:
102
            out = str(td).replace(" days, ", ":")
103
        else:
104
            out = str(td).replace(" day, ", ":")
105
    else:
106
        out = "0:" + str(td)
107
    outAr = out.split(':')
108
    outAr = ["%02d" % (int(float(x))) for x in outAr]
109
    out = ":".join(outAr)
110
    return out
111
112
113
def get_timedelta(date_str):
114
    # Returns timedelta object from string in [DD-[hh:]]mm:ss format
115
    days = 0
116
    hours = 0
117
    minutes = 0
118
    seconds = 0
119
120
    if date_str.count('-') == 1:
121
        days = int(date_str.split('-')[0])
122
        date_str = date_str.partition('-')[2]
123
    if date_str.count(':') == 2:
124
        hours = int(date_str.split(':')[0])
125
        date_str = date_str.partition(':')[2]
126
127
    try:
128
        minutes = int(date_str.split(':')[0])
129
        seconds = int(date_str.split(':')[1])
130
    except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
131
        pass
132
133
    return timedelta(days=days,
134
                     hours=hours,
135
                     minutes=minutes,
136
                     seconds=seconds
137
                     )
138
139
140
def get_stats_time(stats):
141
142
    wallclock = '-' if 'wallclock' not in stats else stats['wallclock']
143
    wallclock_delta = None
144
    cputime_delta = None
145
    if wallclock != '-':
146
        try:
147
            wallclock_delta = wallclock
148
            wallclock = print_timedelta(wallclock_delta)
149
        except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
150
            pass
151
    cputime = '-' if 'cpu' not in stats else stats['cpu']
152
    if cputime != '-':
153
        try:
154
            cputime_delta = cputime
155
            cputime = print_timedelta(cputime_delta)
156
        except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
157
            pass
158
159
    time_ratio = None
160
    if cputime_delta and wallclock_delta:
161
        time_ratio = (float(cputime_delta.total_seconds()) /
162
                      wallclock_delta.total_seconds())
163
164
    return cputime, wallclock, time_ratio
165
166
167
def printjobs(num_lines):
168
    print('User name: {0} {1}'.format(job_db.user_db['user'].first_name,
169
                                      job_db.user_db['user'].last_name))
170
    jobs = job_list()
171
    print('     | {0:^10} | {1:^10} |\
172
          {2:^10} | {3:^12} | {4:^12} |\
173
          {5:^5} | {6:^20} | {7:50}'.format('Job ID',
174
                                            'Status',
175
                                            'NTasks',
176
                                            'CPU Time',
177
                                            'Wallclock',
178
                                            'Util %',
179
                                            'Job Name',
180
                                            'Job Dir',)
181
          )
182
    for i, j in enumerate(jobs):
183
        job_id = jobs[j].job_id
184
        status = jobs[j].status
185
        # queue = jobs[j].queue
186
        # site_name = job_db.queue_db[queue].site_name
187
        # scheduler_type = job_db.site_db[site_name].scheduler_type
188
        cputime, wallclock, time_ratio = get_stats_time(jobs[j].stats)
189
        efficiency = '-'
190
        if time_ratio:
191
            try:
192
                efficiency = (time_ratio / (int(jobs[j].num_tasks) *
193
                              int(jobs[j].threads_per_task)) * 100.0)
194
                efficiency = '{:.1f}'.format(efficiency)
195
            except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
196
                pass
197
198
        if status == 'completed':
199
            print('{0:4} | {1:^10} |\
200
                  {2:^10} | {3:^10} |\
201
                  {4:^12} | {5:^12} |\
202
                  {6:^5} | {7:^20} | {8:50}'.format(i + 1,
203
                                                    job_id,
204
                                                    status,
205
                                                    str(jobs[j].num_tasks) +
206
                                                    ' (' +
207
                                                    str(jobs[j].threads_per_task) +
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
208
                                                    ')',
209
                                                    cputime,
210
                                                    wallclock,
211
                                                    efficiency,
212
                                                    jobs[j].job_name,
213
                                                    jobs[j].job_dir)
214
                  )
215
        elif status == 'running':
216
            stats = scheduler.running_stats(job_id)
217
            cputime, wallclock, time_ratio = get_stats_time(stats)
218
            efficiency = '-'
219
            if time_ratio:
220
                try:
221
                    efficiency = (time_ratio / (int(jobs[j].num_tasks) *
222
                                  int(jobs[j].threads_per_task)) * 100.0)
223
                    efficiency = '{:.1f}'.format(efficiency)
224
                except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
225
                    pass
226
            print('{0:4} | {1:^10} | {2:^10} |\
227
                   {3:^10} | {4:^12} | {5:^12} |\
228
                   {6:^5} | {7:^20} | {8:50}'.format(i + 1,
229
                                                     job_id,
230
                                                     status,
231
                                                     str(jobs[j].num_tasks) +
232
                                                     ' (' +
233
                                                     str(jobs[j].threads_per_task) +
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
234
                                                     ')',
235
                                                     cputime,
236
                                                     wallclock,
237
                                                     efficiency,
238
                                                     jobs[j].job_name,
239
                                                     jobs[j].job_dir)
240
                  )
241
        else:
242
            print('{0:4} | {1:^10} | {2:^10} |\
243
                   {3:^10} | {4:^12} | {5:^12} |\
244
                   {6:^5} | {7:^20} | {8:50}'.format(i + 1,
245
                                                     job_id,
246
                                                     status,
247
                                                     str(jobs[j].num_tasks) +
248
                                                     ' (' +
249
                                                     str(jobs[j].threads_per_task) +
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (84/80).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
250
                                                     ')',
251
                                                     '-',
252
                                                     '-',
253
                                                     efficiency,
254
                                                     jobs[j].job_name,
255
                                                     jobs[j].job_dir)
256
                  )
257
258
    remotes = remote_sites()
259
    for i, j in enumerate(remotes):
260
        print('Remote Site: ' + remotes[j].name)
261
        remote_list = remote_job_list(remotes[j].user + '@' + remotes[j].name)
262
        for r in remote_list:
263
            print(remote_list[r])
264
265
266
def print_queue_info():
267
    print('{0:25} | {1:^15} | {2:^15} | {3:^15} |\
268
           {4:^15} | {5:^15}'.format('Queue Name', 'Node Max Task',
269
                                     'Node Max Thread', 'Node Max Memory',
270
                                     'Max Task', 'Available Task'))
271
    for q in queues():
272
        try:
273
            nc = scheduler.node_config(q)
274
            tpn = scheduler.tasks_per_node(q)
275
            avail = scheduler.available_tasks(q)
276
        except:
0 ignored issues
show
Coding Style Best Practice introduced by
General except handlers without types should be used sparingly.

Typically, you would use general except handlers when you intend to specifically handle all types of errors, f.e. when logging. Otherwise, such general error handlers can mask errors in your application that you want to know of.

Loading history...
277
            nc = None
278
            tpn = None
279
            avail = None
280
        print('{0:25} | {1:^15} | {2:^15} |\
281
               {3:^15} | {4:^15} | {5:^15}'.format(q, tpn,
282
                                                   nc['max thread'],
283
                                                   nc['max memory'],
284
                                                   avail['max tasks'],
285
                                                   avail['available']))
286
287
288
def create_submit(queue_id, script_name=None, **kwargs):
289
290
    if job_db is not None:
291
        if 'user_email' not in kwargs:
292
            email = job_db.user_db['user'].email
293
            if email != 'unknown':
294
                kwargs['user_email'] = email
295
296
    if scheduler is not None:
297
        script = scheduler.create_submit(queue_id, **kwargs)
298
299
        if script_name is not None:
300
            import os.path
0 ignored issues
show
Comprehensibility Bug introduced by
os is re-defining a name which is already available in the outer-scope (previously defined on line 2).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
301
            if not os.path.isfile(script_name):
302
                with open(script_name, 'w') as f:
303
                    f.write(script)
304
            else:
305
                print('Warning file: {0} already exists.\
306
                       Please choose a different name'.format(script_name))
307
        return script
308
    else:
309
        print('Warning job scheduler not detected')
310
        return None
311
312
313
def submit(script_name, immediate, depends=None):
314
315
    if scheduler is None:
316
        return None
317
318
    job_id = -1
319
    import os.path
0 ignored issues
show
Comprehensibility Bug introduced by
os is re-defining a name which is already available in the outer-scope (previously defined on line 2).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
320
    if os.path.isfile(script_name):
321
        job_id = scheduler.submit(script_name, immediate, depends)
322
        if job_id is not None:
323
            print('Job submitted with ID {0}'.format(job_id))
324
        if job_db is not None and job_id is not None:
325
            from persist import Job
0 ignored issues
show
Configuration introduced by
The import persist could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
326
            job = Job(job_id, time.time())
327
            with open(script_name, 'r') as f:
328
                for line in f:
329
                    if line.split('=')[0] == 'export NUM_TASKS':
330
                        job.num_tasks = line.split('=')[1].strip()
331
                    if line.split('=')[0] == 'export TASKS_PER_NODE':
332
                        job.tasks_per_node = line.split('=')[1].strip()
333
                    if line.split('=')[0] == 'export THREADS_PER_TASK':
334
                        job.threads_per_task = line.split('=')[1].strip()
335
                    if line.split('=')[0] == 'export NUM_NODES':
336
                        job.num_nodes = line.split('=')[1].strip()
337
                    if line.split('=')[0] == 'export MYCLUSTER_QUEUE':
338
                        job.queue = line.split('=')[1].strip()
339
                    if line.split('=')[0] == 'export MYCLUSTER_JOB_NAME':
340
                        job.job_name = line.split('=')[1].strip()
341
342
            job.script_name = script_name
343
            job.job_dir = os.path.dirname(os.path.abspath(script_name))
344
            job_db.add_job(job)
345
            job_db.add_queue(job.queue, scheduler.name())
346
    else:
347
        print('Error file: {0} does not exist.'.format(script_name))
348
349
    return job_id
350
351
352
def delete(job_id):
353
    # Add check
354
    job = job_db.get(job_id)
355
    site_name = job.queue.site_name
356
    scheduler_type = job_db.site_db[site_name].scheduler_type
357
358
    if (scheduler.name() == site_name and
359
       scheduler.scheduler_type() == scheduler_type):
360
        scheduler.delete(job_id)
361
    else:
362
        print('JobID: ' + str(job_id) + ' not found at current site')
363
364
365
def add_remote(remote_site):
366
    if job_db is not None:
367
        job_db.add_remote(remote_site)
368
369
370
def export(job_id):
371
    pass
372
373
374
def job_list():
375
    if job_db is not None:
376
        return job_db.job_db
377
    return []
378
379
380
def get_job(job_id):
381
    if job_db is not None:
382
        return job_db.get(job_id)
383
    return None
384
385
386
def my_import(name):
387
    mod = __import__(name)
388
    components = name.split('.')
389
    for comp in components[1:]:
390
        mod = getattr(mod, comp)
391
    return mod
392
393
394
def get_directory():
395
    from os.path import expanduser
396
    home = expanduser("~")
397
    directory = home + '/.mycluster/'
398
    return directory
399
400
401
def create_directory():
402
    directory = get_directory()
403
    if not os.path.exists(directory):
404
        os.makedirs(directory)
405
        return True
406
    else:
407
        return False
408
409
410
def create_db():
411
    global job_db
412
    try:
413
        from persist import JobDB
0 ignored issues
show
Configuration introduced by
The import persist could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
414
        job_db = JobDB()
415
    except Exception as e:
416
        print('Database failed to initialise. Error Message: ' + str(e))
417
418
    return job_db
419
420
421
def update_db():
422
    try:
423
        if scheduler is not None:
424
            status_dict = scheduler.status()
425
            jobs = job_list()
426
            for j in jobs:
427
                if jobs[j].status != 'completed':
428
                    job_id = jobs[j].job_id
429
                    if job_id in status_dict:
430
                        state = status_dict[job_id]
431
                        if state == 'r':
432
                            jobs[j].update_status('running')
433
                    else:
434
                        jobs[j].update_status('completed')
435
                        jobs[j].update_stats(scheduler.job_stats(job_id))
436
    except Exception as e:
437
        print('Database failed to update. Error Message: ' + str(e))
438
439
440
def sysscribe_update(job_id):
441
    if job_db is not None:
442
        from sysscribe import system
0 ignored issues
show
Configuration introduced by
The import sysscribe could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
443
        job_db.get(job_id).update_sysscribe(system.system_dict())
444
445
446
def email_update(email):
447
    if job_db is not None:
448
        job_db.user_db['user'].update_email(email)
449
450
451
def firstname_update(name):
452
    if job_db is not None:
453
        job_db.user_db['user'].firstname(name)
454
455
456
def lastname_update(name):
457
    if job_db is not None:
458
        job_db.user_db['user'].lastname(name)
459
460
461
def get_user():
462
    if job_db is not None:
463
        return (job_db.user_db['user'].first_name + ' ' +
464
                job_db.user_db['user'].last_name)
465
    else:
466
        return 'unknown'
467
468
469
def get_email():
470
    if job_db is not None:
471
        return job_db.user_db['user'].email
472
    else:
473
        return 'unknown'
474
475
476
def get_site():
477
    return 'unknown'
478
479
480
def appname_update(job_id, appname):
481
    if job_db is not None:
482
        job_db.get(job_id).appname(appname)
483
484
485
def appdata_update(job_id, appdata):
486
    if job_db is not None:
487
        job_db.get(job_id).appdata(appdata)
488
489
490
def init():
491
    global scheduler
492
    scheduler = detect_scheduling_sys()
493
    created = create_directory()
494
    if create_db() is not None:
495
        update_db()
496
497
    print('MyCluster Initialisation Info')
498
    print('-----------------------------')
499
    print('Local database in: ' + get_directory())
500
    print('User: ' + get_user())
501
    print('Email: ' + get_email())
502
    if not scheduler:
503
        print('Local job scheduler: None')
504
    else:
505
        print('Local job scheduler: ' + scheduler.scheduler_type())
506
        print('Site name: ' + get_site())
507
    print('')
508