def _update_vms_and_gateways(self, specific_vm_name: str = None, specific_gateway_name: str = None): with self._update_lock: d, timestamp = _vine_net_exec('vineservice.list.vms', { 'testbed_id': str(self.testbed_id) }) for gateway in self.gateways.values(): # type: MutableVineGateway if str(gateway.gateway_id) in d['gateways']: gateway.gateway_details = objectify(d['gateways'][str(gateway.gateway_id)], VineserviceGatewayDetails) for vm in self.vms.values(): # type: MutableVineVm if str(vm.testbed_id) in d['vms']: vm.vm_details = objectify(d['vms'][str(vm.vm_id)], VineVmDetails) if specific_gateway_name is not None: gateway_id = list({k for k in d['gateways'] if d['gateways'][k]['name'] == specific_gateway_name})[0] gateway_details = objectify(d['gateways'][gateway_id], VineserviceGatewayDetails) gateway = MutableVineGateway(testbed_id=int(self.testbed_id), gateway_id=int(gateway_id), gateway_details=gateway_details) self.gateways[gateway_details.gateway_name] = gateway if specific_vm_name is not None: vm_id = list({k for k in d['vms'] if d['vms'][k]['vm_name'] == specific_vm_name})[0] vm_details = objectify(d['vms'][vm_id], VineVmDetails) vm = MutableVineVm(testbed_id=int(self.testbed_id), vm_id=int(vm_id), vm_details=vm_details) self.vms[vm_details.vm_name] = vm
async def _make_request(command: str, machine_readable: bool, request_params: Dict): global _last_msg, _stdout_websocket_identifier await _connect_stdout() param_tuples = () for key in request_params: param_tuples = param_tuples + ((key, request_params[key]),) if _stdout_websocket_identifier is not None: param_tuples = param_tuples + (('stdout_identifier', _stdout_websocket_identifier),) msg = None async with websockets.connect( SERVER_URL + '/' + command + '?' + parse.urlencode(param_tuples), timeout=86400) as websocket: try: while True: msg = await websocket.recv() if machine_readable: print(msg) else: rcv_data = objectify(msg, StatusNotification) # type: StatusNotification print(rcv_data.message) except websockets.ConnectionClosed: if _stdout_websocket is not None: await _stdout_websocket.close() _last_msg = msg
async def _wait_for_update(testbed_id: int, since: int, source_type: str, source_id: int, finished_message_regex: str, timeout_s: int, failed_message_regex: str = None): # TODO: Increment the "since" value for each check elapsed = 0 while elapsed < timeout_s: response, timestamp = _vine_net_exec('vineservice.get.updates', { 'since': since, 'testbed_id': testbed_id }) for event_dict in response['events']: event = objectify(event_dict, VineserviceEvent) # type: VineserviceEvent if event.source_type == source_type and event.source_id == source_id: if re.match(finished_message_regex, event.message) is not None: return elif failed_message_regex is not None and re.match(failed_message_regex, event.message) is not None: raise Exception('Failed occurred while waiting for update! testbed_id: ' + str(testbed_id) + ', source_type: ' + source_type + ', source_id: ' + str(source_id) + ', message_regex: ' + failed_message_regex) await asyncio.sleep(1) elapsed = elapsed + 1 raise Exception('Timeout of ' + str(timeout_s) + ' occurred while waiting for update! testbed_id: ' + str(testbed_id) + ', source_type: ' + source_type + ', source_id: ' + str(source_id) + ', message_regex: ' + finished_message_regex)
def _get_gateway(gateway_name: str, testbed_id: int): d, timestamp = _vine_net_exec('vineservice.list.gateways', { 'testbed_id': testbed_id }) gateway_id = list(filter(lambda x: d['gateways'][x]['name'] == gateway_name, d['gateways'].keys()))[0] gateway_details = objectify(d['gateways'][gateway_id], VineserviceGatewayDetails) return MutableVineGateway(gateway_id=gateway_id, testbed_id=testbed_id, gateway_details=gateway_details)
def _get_vm(testbed_id: int, vm_name: str) -> MutableVineVm: d, timestamp = _vine_net_exec('vineservice.list.vms', { 'testbed_id': str(testbed_id) }) vm_id = list(filter(lambda x: d['vms'][x]['vm_name'] == vm_name, d['vms'].keys()))[0] vm_details = objectify(d['vms'][vm_id], VineVmDetails) return MutableVineVm(vm_id=int(vm_id), testbed_id=testbed_id, vm_details=vm_details)
async def add_vms(self, vms: List[MutableVineVm], node_numbers: List[int]): timestamp = str(int(time.time() * 1000)) _vine_net_exec('vineservice.get.updates', { 'since': timestamp, 'testbed_id': self.testbed_id }) pending_entities = {} for idx in range(0, len(vms)): vm = vms[idx] ip_address = self.gateway_details.gateway[0:self.gateway_details.gateway.rfind('.') + 1] + str( node_numbers[idx]) pending_entities[vm.vm_id] = { "vm_name": vm.vm_details.vm_name, "ni_obj": { "pending_add_auto": {}, "pending_add_user": { "nic0": { "gateway_id": str( self.gateway_id), "ip_address": ip_address, "status": "pending_add" } }, "pending_delete": {} }, "status": "pending_edit_nw"} data, timestamp = _vine_net_exec('vineservice.apply.testbed', {"testbed_id": self.testbed_id, "pending_entities": pending_entities }) _vine_net_exec('vineservice.edit.gateway', { "net_address": self.gateway_details.net_address, "is_vm": False, "max_hosts": self.gateway_details.max_hosts, "gateway": self.gateway_details.gateway, "used_hosts": self.gateway_details.used_hosts, "bcast_address": self.gateway_details.bcast_address, "netmask": self.gateway_details.netmask, "name": self.gateway_details.name, "uuid": self.gateway_details.uuid, "created": None, "modified": None, "dummy": False, "id": str(self.gateway_id) }) task = objectify(data, VineTask) # type: VineTask return await _wait_for_task_finish(vine_task=task)
def create_testbed(testbed_name: str, testbed_desc: str) -> MutableVineTestbed: d, timestamp = _vine_net_exec('vineservice.add.testbed', { "testbed_name": testbed_name, "testbed_group": VINE_GROUP, "testbed_desc": testbed_desc}) # def __init__(self, testbed_id: int, testbed: Dict[str, VineTestbedDetails], status: int): testbed_id = d['testbed_id'] testbed_details = objectify(d['testbed'][str(testbed_id)], VineTestbedDetails) return MutableVineTestbed(testbed_id=testbed_id, testbed_details=testbed_details, vms=dict(), gateways=dict())
def get_testbed(testbed_name: str) -> MutableVineTestbed: try: d, timestamp = _vine_net_exec('vineservice.list.testbeds', None) testbed_id = list(filter(lambda x: d['testbeds'][x]['testbed_name'] == testbed_name, d['testbeds'].keys()))[0] return MutableVineTestbed(testbed_id=int(testbed_id), testbed_details=objectify(d['testbeds'][testbed_id], VineTestbedDetails), vms=dict(), gateways=dict()) except Exception as e: print(str(e)) traceback.print_exc() raise BuildServiceException(ActivityStatus.SERVER_ERROR, testbed_name, message='Could not get testbed with the name "' + testbed_name + "'!")
def get_vms_by_id(testbed_id: int) -> List[MutableVineVm]: rval = list() d, timestamp = _vine_net_exec('vineservice.list.vms', { 'testbed_id': str(testbed_id) }) for vm_id in d['vms'].keys(): vm_details = objectify(d['vms'][str(vm_id)], VineVmDetails) rval.append(MutableVineVm( vm_id=int(vm_id), testbed_id=testbed_id, vm_details=vm_details )) return rval
def get_testbeds_by_ids(testbed_ids: List[int]) -> List[MutableVineTestbed]: rval = list() d, timestamp = _vine_net_exec('vineservice.list.testbeds', None) for testbed_id in testbed_ids: if str(testbed_id) in d['testbeds']: testbed_vms = get_vms_by_id(testbed_id=testbed_id) testbed_dict = dict() for testbed_vm in testbed_vms: testbed_dict[testbed_vm.vm_details.vm_name] = testbed_vm rval.append(MutableVineTestbed(testbed_id=int(testbed_id), testbed_details=objectify(d['testbeds'][str(testbed_id)], VineTestbedDetails), vms=testbed_dict, gateways=dict())) else: print('Testbed with id ' + str(testbed_id) + ' no longer exists on the vine cluster.') return rval
def create_unmanaged_testbed(include_android: bool, autoconf: bool): if autoconf and not os.path.exists(SSH_KEY_PATH): print('ERROR: Please add an authorized key to testbed images and ensure the private key exists at "' + SSH_KEY_PATH + '"!') exit(1) asyncio.get_event_loop().run_until_complete(_make_request( 'createTestbed', mm, {'testbed_name': user + '_' + parser_args.testbed_name, 'include_android': include_android} )) if autoconf: rx_msg_data = json.loads(_last_msg) if 'status' not in rx_msg_data or rx_msg_data['status'] != 'TESTBED_READY' or rx_msg_data['data'] is None: print('Cannot create autoconf file since the server did not return all required information!') rx_data = rx_msg_data['data'] rx_tb = objectify(rx_data, DasTestbed) save_ssh_config_and_display_usage(rx_tb, parser_args.testbed_name.lower())
def _get_latest_matching_images(regex_match_strings: List[str]) -> Dict[str, VineImage]: rval = dict() existing_image_data = _vine_net_exec('vineservice.list.images', {'show_passwords': False})[0]['images'] for regex in regex_match_strings: latest_image_dict = None for key in existing_image_data: image_name = existing_image_data[key]['image_name'] # type: str if re.match(regex, image_name) and ( latest_image_dict is None or image_name >= latest_image_dict['image_name']): latest_image_dict = existing_image_data[key] latest_image_dict['image_id'] = key if latest_image_dict is None: raise Exception("Could not find an image matching regex '" + regex + "'!") rval[regex] = objectify(latest_image_dict, VineImage) return rval
def update_testbeds_details(testbeds: List[MutableVineTestbed]): d, timestamp = _vine_net_exec('vineservice.list.testbeds', None) for tb in testbeds: if str(tb.testbed_id) in d['testbeds']: tb.testbed_details = objectify(d['testbeds'][str(tb.testbed_id)], VineTestbedDetails)
def _get_testbed_by_id(testbed_id: int): d, timestamp = _vine_net_exec('vineservice.list.testbeds', None) return MutableVineTestbed(testbed_id=testbed_id, testbed_details=objectify(d['testbeds'][str(testbed_id)], VineTestbedDetails), vms=dict(), gateways=dict())
async def _vine_net_exec_wait_for_task_finish(method: str, params: Dict[str, object] = None) \ -> Tuple[VineTaskStatus, int]: data, timestamp = _vine_net_exec(method=method, params=params) task = objectify(data, VineTask) # type: VineTask # task_id = data['task_id'] return await _wait_for_task_finish(vine_task=task)
def main(parser_args): import importlib.util missing_libs = list() if importlib.util.find_spec('requests') is None: missing_libs.append('requests==2.18.4') if importlib.util.find_spec('sanic') is None: missing_libs.append('sanic==0.7.0') if importlib.util.find_spec('websockets') is None: missing_libs.append('websockets==6.0') if len(missing_libs) > 0: print("ERROR: Missing required libraries! Please install using the following command::") print(' ' + sys.executable + ' -m pip install ' + ' '.join(missing_libs)) exit(1) import websockets from buildsystem.datatypes import objectify, StatusNotification from buildsystem.testbedmanager import DasTestbed from buildsystem.vine import VINE_ROOT_URL if parser_args.verbose: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) async def _connect_stdout(): global _stdout_websocket, _stdout_websocket_identifier async def print_from_websocket(ws): try: while True: msg = await ws.recv() print(msg) except websockets.ConnectionClosed: pass websocket = await websockets.connect(SERVER_URL + '/stdoutListener', timeout=86400) _stdout_websocket = websocket _stdout_websocket_identifier = await websocket.recv() asyncio.ensure_future(print_from_websocket(websocket)) async def _make_request(command: str, machine_readable: bool, request_params: Dict): global _last_msg, _stdout_websocket_identifier await _connect_stdout() param_tuples = () for key in request_params: param_tuples = param_tuples + ((key, request_params[key]),) if _stdout_websocket_identifier is not None: param_tuples = param_tuples + (('stdout_identifier', _stdout_websocket_identifier),) msg = None async with websockets.connect( SERVER_URL + '/' + command + '?' + parse.urlencode(param_tuples), timeout=86400) as websocket: try: while True: msg = await websocket.recv() if machine_readable: print(msg) else: rcv_data = objectify(msg, StatusNotification) # type: StatusNotification print(rcv_data.message) except websockets.ConnectionClosed: if _stdout_websocket is not None: await _stdout_websocket.close() _last_msg = msg def create_unmanaged_testbed(include_android: bool, autoconf: bool): if autoconf and not os.path.exists(SSH_KEY_PATH): print('ERROR: Please add an authorized key to testbed images and ensure the private key exists at "' + SSH_KEY_PATH + '"!') exit(1) asyncio.get_event_loop().run_until_complete(_make_request( 'createTestbed', mm, {'testbed_name': user + '_' + parser_args.testbed_name, 'include_android': include_android} )) if autoconf: rx_msg_data = json.loads(_last_msg) if 'status' not in rx_msg_data or rx_msg_data['status'] != 'TESTBED_READY' or rx_msg_data['data'] is None: print('Cannot create autoconf file since the server did not return all required information!') rx_data = rx_msg_data['data'] rx_tb = objectify(rx_data, DasTestbed) save_ssh_config_and_display_usage(rx_tb, parser_args.testbed_name.lower()) def save_ssh_config_and_display_usage(target_testbed: DasTestbed, config_identifier: str = None): if not os.path.exists(SSH_PATH): print('Cannot create autoconf file since the directory "' + SSH_PATH + '" does not exist!') return das_ip = target_testbed.get_das_vm().vm_details.public_ip has_android = target_testbed.has_android_vms() testbed_id = target_testbed.testbed.testbed_id testbed_name = target_testbed.testbed.testbed_details.testbed_name if config_identifier is None: config_identifier = testbed_name.lower() ssh_file_lines = list() ssh_file_lines.append('Host ' + config_identifier + 'das\n') ssh_file_lines.append(' HostName ' + das_ip + '\n') ssh_file_lines.append(' IdentityFile ' + SSH_KEY_PATH + '\n') ssh_file_lines.append(' User ubuntu\n') ssh_file_lines.append(' StrictHostKeyChecking no\n') if has_android: android0_ip = target_testbed.get_android0_vm().vm_details.public_ip android1_ip = target_testbed.get_android1_vm().vm_details.public_ip ssh_file_lines.append('Host ' + config_identifier + 'android0\n') ssh_file_lines.append(' HostName ' + android0_ip + '\n') ssh_file_lines.append(' IdentityFile ' + SSH_KEY_PATH + '\n') ssh_file_lines.append(' User ubuntu\n') ssh_file_lines.append(' StrictHostKeyChecking no\n') ssh_file_lines.append('Host ' + config_identifier + 'android1\n') ssh_file_lines.append(' HostName ' + android1_ip + '\n') ssh_file_lines.append(' IdentityFile ' + SSH_KEY_PATH + '\n') ssh_file_lines.append(' User ubuntu\n') ssh_file_lines.append(' StrictHostKeyChecking no\n') display_msg = _create_testbed_android_autoconf_msg % ( config_identifier, config_identifier, config_identifier, config_identifier, android1_ip, android0_ip, VINE_ROOT_URL, str(testbed_id)) else: display_msg = _create_testbed_plain_autoconf_msg % ( config_identifier, config_identifier, VINE_ROOT_URL, str(testbed_id)) if len(ssh_file_lines) > 0: with open(os.path.join(SSH_PATH, 'immortals_vine_' + config_identifier + '_config'), 'w') as file: file.writelines(ssh_file_lines) print(display_msg) if re.search('^[a-zA-Z0-9_]*$', parser_args.testbed_name) is None: print("ERROR: Testbed Must contain only nunbers, letters, or an underscore!") exit(1) if parser_args.testbed_name == "null" or parser_args.testbed_name == "None": print('ERROR: "null" and "None" cannot be used for testbed names!') exit(1) mm = parser_args.machine_mode user = os.environ['USER'] if user is None: user = '******' + str(uuid.uuid4())[0:8] # Jekins specific commands if parser_args.jenkins_add_plain_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'addPlainTestbedToBuildPool', mm, {} )) elif parser_args.jenkins_add_android_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'addAndroidTestbedToBuildPool', mm, {} )) elif parser_args.jenkins_replace_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'replaceTestbedInBuildPool', mm, {'testbed_name': 'jenkins_' + parser_args.testbed_name} )) elif parser_args.jenkins_replace_testbed_nowait: asyncio.get_event_loop().run_until_complete(_make_request( 'replaceTestbedInBuildPoolNoWait', mm, {'testbed_name': 'jenkins_' + parser_args.testbed_name} )) elif parser_args.jenkins_claim_plain_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'claimBuildpoolPlainTestbed', mm, {'testbed_name': 'jenkins_' + parser_args.testbed_name} )) elif parser_args.jenkins_claim_android_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'claimBuildpoolAndroidTestbed', mm, {'testbed_name': 'jenkins_' + parser_args.testbed_name} )) elif parser_args.jenkins_start_testbed_coordinator: from buildsystem import server server.main() elif parser_args.jenkins_perform_predeploy_rebuild: raise NotImplementedError # asyncio.get_event_loop().run_until_complete(_make_request( # 'rebuildPredeployImage', mm, # {'testbed_name': user + '_' + parser_args.testbed_name} # )) elif parser_args.jenkins_autoconf: asyncio.get_event_loop().run_until_complete(_make_request( 'getExistingTestbed', mm, {'testbed_name': 'jenkins_' + parser_args.testbed_name} )) msg_data = json.loads(_last_msg) if 'status' not in msg_data or msg_data['status'] != 'TESTBED_READY' or msg_data['data'] is None: print('Cannot create autoconf file since the server did not return all required information!') data = msg_data['data'] testbed = objectify(data, DasTestbed) save_ssh_config_and_display_usage(testbed, 'jenkinstemp') # General commands elif parser_args.create_testbed: create_unmanaged_testbed(False, False) elif parser_args.create_android_testbed: create_unmanaged_testbed(True, False) elif parser_args.delete_testbed: asyncio.get_event_loop().run_until_complete(_make_request( 'deleteTestbed', mm, {'testbed_name': user + '_' + parser_args.testbed_name} )) elif parser_args.create_testbed_autoconf: create_unmanaged_testbed(False, True) elif parser_args.create_android_testbed_autoconf: create_unmanaged_testbed(True, True) elif parser_args.das_repo_update: params = { 'testbed_name': user + '_' + parser_args.testbed_name } if parser_args.branch is not None: params['branch'] = parser_args.branch asyncio.get_event_loop().run_until_complete(_make_request('updateRepo', mm, params)) elif parser_args.das_execute_test is not None: params = { 'testbed_name': user + '_' + parser_args.testbed_name, 'test_identifier': parser_args.das_execute_test } if parser_args.specify_cp_profile is not None: params['cp_profile'] = parser_args.specify_cp_profile if parser_args.branch is not None: params['branch'] = parser_args.branch asyncio.get_event_loop().run_until_complete(_make_request('dasExecuteTest', mm, params)) elif parser_args.das_deploy: params = { 'testbed_name': user + '_' + parser_args.testbed_name, } if parser_args.specify_cp_profile is not None: params['cp_profile'] = parser_args.specify_cp_profile if parser_args.branch is not None: params['branch'] = parser_args.branch asyncio.get_event_loop().run_until_complete(_make_request( 'dasDeploy', mm, params )) else: print('Unexpected parameters.') exit(1) notification = objectify(json.loads(_last_msg), StatusNotification) # type: StatusNotification exit(notification.status.error_code)
def ping() -> VineservicePingResponse: d, timestamp = _vine_net_exec('vineservice.admin', { 'action': 'ping' }) return objectify(d, VineservicePingResponse) # type: VineservicePingResponse