1# setup (you can ignore this) 2# ########################### 3 4# a bit of setup to allow overriding rpc_interace with an RPC proxy 5# (to use RPC, we would say 6# import rpc_client_lib 7# rpc_interface = rpc_client_lib.get_proxy( 8# 'http://hostname:8000/afe/server/noauth/rpc/') 9# ) 10>>> if 'rpc_interface' not in globals(): 11... from autotest_lib.frontend.afe import rpc_interface, models 12... from autotest_lib.frontend import thread_local 13... # set up a user for us to "login" as 14... user = models.User(login='debug_user') 15... user.access_level = 100 16... user.save() 17... thread_local.set_user(user) 18... user2 = models.User(login='showard') 19... user2.access_level = 1 20... user2.save() 21... 22>>> from autotest_lib.frontend.afe import model_logic 23 24# get directory of this test file; we'll need it later 25>>> import common 26>>> from autotest_lib.frontend.afe import test 27>>> import os, datetime 28>>> test_path = os.path.join(os.path.dirname(test.__file__), 29... 'doctests') 30>>> test_path = os.path.abspath(test_path) 31 32# disable logging 33>>> from autotest_lib.client.common_lib import logging_manager 34>>> logging_manager.logger.setLevel(100) 35 36>>> drone_set = models.DroneSet.default_drone_set_name() 37>>> if drone_set: 38... _ = models.DroneSet.objects.create(name=drone_set) 39 40# mock up tko rpc_interface 41>>> from autotest_lib.client.common_lib.test_utils import mock 42>>> mock.mock_god().stub_function_to_return(rpc_interface.tko_rpc_interface, 43... 'get_status_counts', 44... None) 45 46# basic interface test 47###################### 48 49# echo a comment 50>>> rpc_interface.echo('test string to echo') 51'test string to echo' 52 53# basic object management 54# ####################### 55 56# create a label 57>>> rpc_interface.add_label(name='test_label') 581 59 60# we can modify the label by referencing its ID... 61>>> rpc_interface.modify_label(1, kernel_config='/my/kernel/config') 62 63# ...or by referencing it's name 64>>> rpc_interface.modify_label('test_label', platform=True) 65 66# we use get_labels to retrieve object data 67>>> data = rpc_interface.get_labels(name='test_label') 68>>> data == [{'id': 1, 69... 'name': 'test_label', 70... 'platform': 1, 71... 'kernel_config': '/my/kernel/config', 72... 'only_if_needed' : False, 73... 'invalid': 0, 74... 'atomic_group': None}] 75True 76 77# get_labels return multiple matches as lists of dictionaries 78>>> rpc_interface.add_label(name='label1', platform=False) 792 80>>> rpc_interface.add_label(name='label2', platform=True) 813 82>>> rpc_interface.add_label(name='label3', platform=False) 834 84>>> data = rpc_interface.get_labels(platform=False) 85>>> data == [{'id': 2, 'name': 'label1', 'platform': 0, 'kernel_config': '', 86... 'only_if_needed': False, 'invalid': 0, 'atomic_group': None}, 87... {'id': 4, 'name': 'label3', 'platform': 0, 'kernel_config': '', 88... 'only_if_needed': False, 'invalid': 0, 'atomic_group': None}] 89True 90 91# delete_label takes an ID or a name as well 92>>> rpc_interface.delete_label(3) 93>>> rpc_interface.get_labels(name='label2') 94[] 95>>> rpc_interface.delete_label('test_label') 96>>> rpc_interface.delete_label('label1') 97>>> rpc_interface.delete_label('label3') 98>>> rpc_interface.get_labels() 99[] 100 101# all the add*, modify*, delete*, and get* methods work the same way 102# hosts... 103>>> rpc_interface.add_host(hostname='ipaj1', locked=True, lock_reason='Locked device on creation') 1041 105>>> data = rpc_interface.get_hosts() 106 107# delete the lock_time field, since that can't be reliably checked 108>>> del data[0]['lock_time'] 109>>> data == [{'id': 1, 110... 'hostname': 'ipaj1', 111... 'locked': 1, 112... 'synch_id': None, 113... 'status': 'Ready', 114... 'labels': [], 115... 'acls': ['Everyone'], 116... 'platform': None, 117... 'attributes': {}, 118... 'invalid': 0, 119... 'protection': 'No protection', 120... 'locked_by': 'debug_user', 121... 'dirty': True, 122... 'leased': 1, 123... 'shard': None, 124... 'lock_reason': 'Locked device on creation'}] 125True 126>>> rpc_interface.modify_host(id='ipaj1', status='Hello') 127Traceback (most recent call last): 128ValidationError: {'status': 'Host status can not be modified by the frontend.'} 129>>> rpc_interface.modify_host(id='ipaj1', hostname='ipaj1000') 130>>> rpc_interface.modify_hosts( 131... host_filter_data={'hostname': 'ipaj1000'}, 132... update_data={'locked': False}) 133>>> data = rpc_interface.get_hosts() 134>>> bool(data[0]['locked']) 135False 136 137# test already locked/unlocked failures 138>>> rpc_interface.modify_host(id='ipaj1000', locked=False) 139Traceback (most recent call last): 140ValidationError: {'locked': u'Host ipaj1000 already unlocked.'} 141>>> rpc_interface.modify_host(id='ipaj1000', locked=True, lock_reason='Locking a locked device') 142>>> try: 143... rpc_interface.modify_host(id='ipaj1000', locked=True) 144... except model_logic.ValidationError, err: 145... pass 146>>> assert ('locked' in err.message_dict 147... and err.message_dict['locked'].startswith('Host ipaj1000 already locked')) 148>>> rpc_interface.delete_host(id='ipaj1000') 149>>> rpc_interface.get_hosts() == [] 150True 151 152# tests... 153>>> rpc_interface.get_tests() == [] 154True 155 156# profilers... 157>>> rpc_interface.add_profiler(name='oprofile') 1581 159>>> rpc_interface.modify_profiler('oprofile', description='Oh profile!') 160>>> data = rpc_interface.get_profilers() 161>>> data == [{'id': 1, 162... 'name': 'oprofile', 163... 'description': 'Oh profile!'}] 164True 165>>> rpc_interface.delete_profiler('oprofile') 166>>> rpc_interface.get_profilers() == [] 167True 168 169 170# users... 171>>> data = rpc_interface.get_users(login='showard') 172>>> data == [{'id': 2, 173... 'login': 'showard', 174... 'access_level': 1, 175... 'reboot_before': 'If dirty', 176... 'reboot_after': 'Never', 177... 'drone_set': None, 178... 'show_experimental': False}] 179True 180 181# acl groups... 182# 1 ACL group already exists, named "Everyone" (ID 1) 183>>> rpc_interface.add_acl_group(name='my_group') 1842 185>>> rpc_interface.modify_acl_group('my_group', description='my new acl group') 186>>> data = rpc_interface.get_acl_groups(name='my_group') 187>>> data == [{'id': 2, 188... 'name': 'my_group', 189... 'description': 'my new acl group', 190... 'users': ['debug_user'], 191... 'hosts': []}] 192True 193>>> rpc_interface.delete_acl_group('my_group') 194>>> data = rpc_interface.get_acl_groups() 195>>> data == [{'id': 1, 196... 'name': 'Everyone', 197... 'description': '', 198... 'users': ['debug_user', 'showard'], 199... 'hosts': []}] 200True 201 202 203# managing many-to-many relationships 204# ################################### 205 206# first, create some hosts and labels to play around with 207>>> rpc_interface.add_host(hostname='host1') 2082 209>>> rpc_interface.add_host(hostname='host2') 2103 211>>> rpc_interface.add_label(name='label1') 2122 213>>> rpc_interface.add_label(name='label2', platform=True) 2143 215 216# add hosts to labels 217>>> rpc_interface.host_add_labels(id='host1', labels=['label1']) 218>>> rpc_interface.host_add_labels(id='host2', labels=['label1', 'label2']) 219 220# check labels for hosts 221>>> data = rpc_interface.get_hosts(hostname='host1') 222>>> data[0]['labels'] 223[u'label1'] 224>>> data = rpc_interface.get_hosts(hostname='host2') 225>>> data[0]['labels'] 226[u'label1', u'label2'] 227>>> data[0]['platform'] 228u'label2' 229 230# check host lists for labels -- use double underscore to specify fields of 231# related objects 232>>> data = rpc_interface.get_hosts(labels__name='label1') 233>>> [host['hostname'] for host in data] 234[u'host1', u'host2'] 235>>> data = rpc_interface.get_hosts(labels__name='label2') 236>>> [host['hostname'] for host in data] 237[u'host2'] 238 239# remove a host from a label 240>>> rpc_interface.host_remove_labels(id='host2', labels=['label2']) 241>>> data = rpc_interface.get_hosts(hostname='host1') 242>>> data[0]['labels'] 243[u'label1'] 244>>> rpc_interface.get_hosts(labels__name='label2') 245[] 246 247# Cleanup 248>>> rpc_interface.host_remove_labels(id='host2', labels=['label1']) 249>>> rpc_interface.host_remove_labels(id='host1', labels=['label1']) 250 251 252# Other interface for new CLI 253# add hosts to labels 254>>> rpc_interface.label_add_hosts(id='label1', hosts=['host1']) 255>>> rpc_interface.label_add_hosts(id='label2', hosts=['host1', 'host2']) 256 257# check labels for hosts 258>>> data = rpc_interface.get_hosts(hostname='host1') 259>>> data[0]['labels'] 260[u'label1', u'label2'] 261>>> data = rpc_interface.get_hosts(hostname='host2') 262>>> data[0]['labels'] 263[u'label2'] 264>>> data[0]['platform'] 265u'label2' 266 267# check host lists for labels -- use double underscore to specify fields of 268# related objects 269>>> data = rpc_interface.get_hosts(labels__name='label1') 270>>> [host['hostname'] for host in data] 271[u'host1'] 272>>> data = rpc_interface.get_hosts(labels__name='label2') 273>>> [host['hostname'] for host in data] 274[u'host1', u'host2'] 275 276# remove a host from a label 277>>> rpc_interface.label_remove_hosts(id='label2', hosts=['host2']) 278>>> data = rpc_interface.get_hosts(hostname='host1') 279>>> data[0]['labels'] 280[u'label1', u'label2'] 281>>> data = rpc_interface.get_hosts(labels__name='label2') 282>>> [host['hostname'] for host in data] 283[u'host1'] 284 285# Remove multiple hosts from a label 286>>> rpc_interface.label_add_hosts(id='label2', hosts=['host2']) 287>>> data = rpc_interface.get_hosts(labels__name='label2') 288>>> [host['hostname'] for host in data] 289[u'host1', u'host2'] 290>>> rpc_interface.label_remove_hosts(id='label2', hosts=['host2', 'host1']) 291>>> rpc_interface.get_hosts(labels__name='label2') 292[] 293 294 295# ACL group relationships work similarly 296# note that all users are a member of 'Everyone' by default, and that hosts are 297# automatically made a member of 'Everyone' only when they are a member of no 298# other group 299>>> data = rpc_interface.get_acl_groups(hosts__hostname='host1') 300>>> [acl_group['name'] for acl_group in data] 301[u'Everyone'] 302 303>>> rpc_interface.add_acl_group(name='my_group') 3042 305 306>>> rpc_interface.acl_group_add_users('my_group', ['showard']) 307>>> rpc_interface.acl_group_add_hosts('my_group', ['host1']) 308>>> data = rpc_interface.get_acl_groups(name='my_group') 309>>> data[0]['users'] 310[u'debug_user', u'showard'] 311>>> data[0]['hosts'] 312[u'host1'] 313>>> data = rpc_interface.get_acl_groups(users__login='showard') 314>>> [acl_group['name'] for acl_group in data] 315[u'Everyone', u'my_group'] 316 317# note host has been automatically removed from 'Everyone' 318>>> data = rpc_interface.get_acl_groups(hosts__hostname='host1') 319>>> [acl_group['name'] for acl_group in data] 320[u'my_group'] 321 322>>> rpc_interface.acl_group_remove_users('my_group', ['showard']) 323>>> rpc_interface.acl_group_remove_hosts('my_group', ['host1']) 324>>> data = rpc_interface.get_acl_groups(name='my_group') 325>>> data[0]['users'], data[0]['hosts'] 326([u'debug_user'], []) 327>>> data = rpc_interface.get_acl_groups(users__login='showard') 328>>> [acl_group['name'] for acl_group in data] 329[u'Everyone'] 330 331# note host has been automatically added back to 'Everyone' 332>>> data = rpc_interface.get_acl_groups(hosts__hostname='host1') 333>>> [acl_group['name'] for acl_group in data] 334[u'Everyone'] 335 336 337# host attributes 338 339>>> rpc_interface.set_host_attribute('color', 'red', hostname='host1') 340>>> data = rpc_interface.get_hosts(hostname='host1') 341>>> data[0]['attributes'] 342{u'color': u'red'} 343 344>>> rpc_interface.set_host_attribute('color', None, hostname='host1') 345>>> data = rpc_interface.get_hosts(hostname='host1') 346>>> data[0]['attributes'] 347{} 348 349 350# host bulk modify 351################## 352 353>>> rpc_interface.modify_hosts( 354... host_filter_data={'hostname__in': ['host1', 'host2']}, 355... update_data={'locked': True, 'lock_reason': 'Locked for testing'}) 356>>> data = rpc_interface.get_hosts(hostname__in=['host1', 'host2']) 357 358>>> data[0]['locked'] 359True 360>>> data[1]['locked'] 361True 362 363>>> rpc_interface.modify_hosts( 364... host_filter_data={'id': 2}, 365... update_data={'locked': False}) 366>>> data = rpc_interface.get_hosts(hostname__in=['host1', 'host2']) 367 368>>> data[0]['locked'] 369False 370>>> data[1]['locked'] 371True 372 373 374# job management 375# ############ 376 377# note that job functions require job IDs to identify jobs, since job names are 378# not unique 379 380# add some entries to play with 381>>> rpc_interface.add_label(name='my_label', kernel_config='my_kernel_config') 3825 383>>> rpc_interface.add_host(hostname='my_label_host1') 3844 385>>> rpc_interface.add_host(hostname='my_label_host2') 3865 387>>> rpc_interface.label_add_hosts(id='my_label', hosts=['my_label_host1', 'my_label_host2']) 388 389# generate a control file from existing body text. 390>>> cf_info_pi = rpc_interface.generate_control_file( 391... client_control_file='print "Hi"\n') 392>>> print cf_info_pi['control_file'] #doctest: +NORMALIZE_WHITESPACE 393def step_init(): 394 job.next_step('step0') 395def step0(): 396 print "Hi" 397 return locals() 398 399# create a job to run on host1, host2, and any two machines in my_label 400>>> rpc_interface.create_job(name='my_job', 401... priority=10, 402... control_file=cf_info_pi['control_file'], 403... control_type='Client', 404... hosts=['host1', 'host2'], 405... meta_hosts=['my_label', 'my_label']) 4061 407 408# get job info - this does not include status info for particular hosts 409>>> data = rpc_interface.get_jobs() 410>>> data = data[0] 411>>> data['id'], data['owner'], data['name'], data['priority'] 412(1, u'debug_user', u'my_job', 10) 413>>> data['control_file'] == cf_info_pi['control_file'] 414True 415>>> data['control_type'] 416'Client' 417 418>>> today = datetime.date.today() 419>>> data['created_on'].startswith( 420... '%d-%02d-%02d' % (today.year, today.month, today.day)) 421True 422 423# get_num_jobs - useful when dealing with large numbers of jobs 424>>> rpc_interface.get_num_jobs(name='my_job') 4251 426 427# check host queue entries for a job 428>>> data = rpc_interface.get_host_queue_entries(job=1) 429>>> len(data) 4304 431 432# get rid of created_on, it's nondeterministic 433>>> data[0]['job']['created_on'] = data[2]['job']['created_on'] = None 434 435# get_host_queue_entries returns full info about the job within each queue entry 436>>> job = data[0]['job'] 437>>> job == {'control_file': cf_info_pi['control_file'], # the control file we used 438... 'control_type': 'Client', 439... 'created_on': None, 440... 'id': 1, 441... 'name': 'my_job', 442... 'owner': 'debug_user', 443... 'priority': 10, 444... 'synch_count': 0, 445... 'timeout': 24, 446... 'timeout_mins': 1440, 447... 'max_runtime_mins': 1440, 448... 'max_runtime_hrs' : 72, 449... 'run_verify': False, 450... 'run_reset': True, 451... 'email_list': '', 452... 'reboot_before': 'If dirty', 453... 'reboot_after': 'Never', 454... 'parse_failed_repair': True, 455... 'drone_set': drone_set, 456... 'parameterized_job': None, 457... 'test_retry': 0, 458... 'parent_job': None, 459... 'shard': None, 460... 'require_ssp': None} 461True 462 463# get_host_queue_entries returns a lot of data, so let's only check a couple 464>>> data[0] == ( 465... {'active': 0, 466... 'complete': 0, 467... 'host': {'hostname': 'host1', # full host info here 468... 'id': 2, 469... 'invalid': 0, 470... 'locked': 0, 471... 'status': 'Ready', 472... 'synch_id': None, 473... 'protection': 'No protection', 474... 'locked_by': None, 475... 'lock_time': None, 476... 'lock_reason': 'Locked for testing', 477... 'dirty': True, 478... 'leased': 1, 479... 'shard': None}, 480... 'id': 1, 481... 'job': job, # full job info here 482... 'meta_host': None, 483... 'status': 'Queued', 484... 'deleted': 0, 485... 'execution_subdir': '', 486... 'atomic_group': None, 487... 'aborted': False, 488... 'started_on': None, 489... 'finished_on': None, 490... 'full_status': 'Queued'}) 491True 492>>> data[2] == ( 493... {'active': 0, 494... 'complete': 0, 495... 'host': None, 496... 'id': 3, 497... 'job': job, 498... 'meta_host': 'my_label', 499... 'status': 'Queued', 500... 'deleted': 0, 501... 'execution_subdir': '', 502... 'atomic_group': None, 503... 'aborted': False, 504... 'started_on': None, 505... 'finished_on': None, 506... 'full_status': 'Queued'}) 507True 508>>> rpc_interface.get_num_host_queue_entries(job=1) 5094 510>>> rpc_interface.get_hqe_percentage_complete(job=1) 5110.0 512 513# get_jobs_summary adds status counts to the rest of the get_jobs info 514>>> data = rpc_interface.get_jobs_summary() 515>>> counts = data[0]['status_counts'] 516>>> counts 517{u'Queued': 4} 518 519# abort the job 520>>> data = rpc_interface.abort_host_queue_entries(job__id=1) 521>>> data = rpc_interface.get_jobs_summary(id=1) 522>>> data[0]['status_counts'] 523{u'Aborted (Queued)': 4} 524 525# Remove the two hosts in my_label 526>>> rpc_interface.delete_host(id='my_label_host1') 527>>> rpc_interface.delete_host(id='my_label_host2') 528 529 530# extra querying parameters 531# ######################### 532 533# get_* methods can take query_start and query_limit arguments to implement 534# paging and a sort_by argument to specify the sort column 535>>> data = rpc_interface.get_hosts(query_limit=1) 536>>> [host['hostname'] for host in data] 537[u'host1'] 538>>> data = rpc_interface.get_hosts(query_start=1, query_limit=1) 539>>> [host['hostname'] for host in data] 540[u'host2'] 541 542# sort_by = ['-hostname'] indicates sorting in descending order by hostname 543>>> data = rpc_interface.get_hosts(sort_by=['-hostname']) 544>>> [host['hostname'] for host in data] 545[u'host2', u'host1'] 546 547 548# cloning a job 549# ############# 550 551>>> job_id = rpc_interface.create_job(name='my_job_to_clone', 552... priority=50, 553... control_file=cf_info_pi['control_file'], 554... control_type='Client', 555... hosts=['host2'], 556... synch_count=1) 557>>> info = rpc_interface.get_info_for_clone(job_id, False) 558>>> info['meta_host_counts'] 559{} 560>>> info['job']['dependencies'] 561[] 562>>> info['job']['priority'] 56350 564 565 566# advanced usage 567# ############## 568 569# synch_count 570>>> job_id = rpc_interface.create_job(name='my_job', 571... priority=10, 572... control_file=cf_info_pi['control_file'], 573... control_type='Server', 574... synch_count=2, 575... hosts=['host1', 'host2']) 576 577>>> data = rpc_interface.get_jobs(id=job_id) 578>>> data[0]['synch_count'] 5792 580 581# get hosts ACL'd to a user 582>>> hosts = rpc_interface.get_hosts(aclgroup__users__login='debug_user') 583>>> sorted([host['hostname'] for host in hosts]) 584[u'host1', u'host2'] 585 586>>> rpc_interface.add_acl_group(name='mygroup') 5873 588>>> rpc_interface.acl_group_add_users('mygroup', ['debug_user']) 589>>> rpc_interface.acl_group_add_hosts('mygroup', ['host1']) 590>>> data = rpc_interface.get_acl_groups(name='Everyone')[0] 591>>> data['users'], data['hosts'] 592([u'debug_user', u'showard'], [u'host2']) 593>>> data = rpc_interface.get_acl_groups(name='mygroup')[0] 594>>> data['users'], data['hosts'] 595([u'debug_user'], [u'host1']) 596 597>>> hosts = rpc_interface.get_hosts(aclgroup__users__login='debug_user') 598>>> sorted([host['hostname'] for host in hosts]) 599[u'host1', u'host2'] 600>>> hosts = rpc_interface.get_hosts(aclgroup__users__login='showard') 601>>> [host['hostname'] for host in hosts] 602[u'host2'] 603 604>>> rpc_interface.delete_acl_group('mygroup') 605>>> data = rpc_interface.get_acl_groups(name='Everyone')[0] 606>>> sorted(data['hosts']) 607[u'host1', u'host2'] 608