def wait_for_webel(xpath=None, did=None, css=None, negated=False, timeout=10, interval=1, usedin=None, *args, **kwargs): """Wait for an element or a suite of elements (defined by xpath/css/did) to appear or disappear. @param xpath, css, did: At least one Mandatory. Defaulting to xpath. takes an xpath, css or an webel id to click on @type xpath, css, did: str @type negated: bool @param negated: to wait for the element(s) to not be there anymore """ usedin = "{0}/waitforwebel/".format(usedin if usedin else "") prop = ['text', 'is_displayed'] def to_be(): x = webel_grab(xpath=xpath, did=did, css=css, prop=prop, *args, **kwargs) return (x == [] and negated) or (x and not negated) wait(to_be, interval=interval, timeout=timeout, progress_cb=lambda x: '{0}Still looking for els...'.format(usedin), timeout_message="%sDid Not Find els in {0}s" % (usedin)) LOG.debug("{0}Waited for elements in [{1}]{2}...".format(usedin, xpath or css or did, "/negated" if negated else ""))
def setup(self): def wait_on_uninstall(ret): if ret and ret.serviceInstanceRuntimeInfos and ret.serviceInstanceRuntimeInfos.serviceInstanceRuntimeInfo: if isinstance(ret.serviceInstanceRuntimeInfos.serviceInstanceRuntimeInfo, list): for sir in ret.serviceInstanceRuntimeInfos.serviceInstanceRuntimeInfo: if int(sir.id)==self.service_runtime_id: return (sir.status == 'OUT_OF_SERVICE' and \ sir.installState == 'NOT_INSTALLED') else: return (ret.serviceInstanceRuntimeInfos.serviceInstanceRuntimeInfo.status == 'OUT_OF_SERVICE' and \ ret.serviceInstanceRuntimeInfos.serviceInstanceRuntimeInfo.installState == 'NOT_INSTALLED') return True LOG.debug("Uninstalling runtime id = {0} on NSX".format(self.service_runtime_id)) try: self.nsx_rst_api.post(ServiceInstances.RUNTIME_ITEM_URI % (self.service_instance_id, self.service_runtime_id), params_dict={'action': 'uninstall'}) except NetxResourceError: pass # ignore on error as on return we check for uninstall state else fail wait(lambda: self.nsx_rst_api.get(ServiceInstances.RUNTIME_URI % (self.service_instance_id)), condition=wait_on_uninstall, progress_cb=lambda x: "Waiting for runtime id={0} to uninstall".format(self.service_runtime_id), timeout=20, interval=4, timeout_message="runtime, id={0} could not be uninstalled".format(self.service_runtime_id))
def self(self): LOG.debug("Checking NSX service manager id = {0}".format(self.manager_id)) wait(lambda x: self.nsx_rst_api.get(ServiceManagers.URI), condition=lambda ret: self.manager_id in [sm.objectId for sm in ret.serviceManagers.serviceManager if sm.objectId==self.manager_id], progress_cb=lambda x: "Waiting on service manager deletion, id={0}".format(self.manager_id), timeout=20, interval=4, timeout_message="service manager, id={0} was not removed".format(self.manager_id)) LOG.debug("NSX service manager id = {0} deleted on NSX".format(self.manager_id))
def setup(self): LOG.debug('Checking for the virtual server = {0} from NSX'.format(self.virtual_server_id)) wait(lambda: self.nsx_rst_api.get(LoadBalancer.URI % self.edge_id), condition=lambda ret: ret.loadBalancer.virtualServer is None, progress_cb=lambda x: "Waiting to remove virtual server on edge, id={0}".format(self.edge_id), timeout=20, interval=4, timeout_message="Virtual Server was not removed on edge, id={0}".format(self.edge_id)) LOG.debug('Removed the virtual server, id = {0} from NSX'.format(self.virtual_server_id))
def setup(self): LOG.info("WebCert started...") fqdn, _, ip_list = socket.gethostbyname_ex(self.address) aliases = set([x for x in self.options.alias + ip_list + [fqdn]]) pkey, cert = self.gen_certificate(self.address, alt_names=aliases) # Sometimes it fails due to voodoo race conditions. That's why we wait()! wait(lambda: self.push_certificate(pkey, cert), timeout=self.options.timeout) LOG.info("Done.")
def self(self): LOG.debug("Delete NSX service manager id = {0}".format(self.manager_id)) try: self.nsx_rst_api.delete(ServiceManagers.ITEM_URI % self.manager_id) except NetxResourceError: pass wait(lambda x: self.nsx_rst_api.get(ServiceManagers.URI), condition=lambda ret: self.manager_id in [sm.objectId for sm in ret.serviceManagers.serviceManager if sm.objectId==self.manager_id], progress_cb=lambda x: "Waiting on service manager deletion, id={0}".format(self.manager_id), timeout=20, interval=4, timeout_message="service manager, id={0} was not removed".format(self.manager_id))
def setup(self): LOG.debug("Deleting runtime id = {0} on NSX".format(self.service_runtime_id)) try: self.nsx_rst_api.delete(ServiceInstances.RUNTIME_ITEM_DELETE_URI % (self.service_instance_id, self.service_runtime_id)) except NetxResourceError: pass # ignore on error as on return we check for delete state else fail wait(lambda: self.nsx_rst_api.get(ServiceInstances.RUNTIME_URI % (self.service_instance_id)), condition=lambda ret: ret.serviceInstanceRuntimeInfos is None, progress_cb=lambda x: "Waiting for runtime id={0} to be deleted".format(self.service_runtime_id), timeout=20, interval=4, timeout_message="runtime, id={0} could not be deleted".format(self.service_runtime_id))
def setup(self): LOG.debug('Removing the virtual server = {0} from NSX'.format(self.virtual_server_id)) try: self.nsx_rst_api.put(LoadBalancer.URI % self.edge_id, payload=self.pay_load) except NetxResourceError: pass wait(lambda: self.nsx_rst_api.get(LoadBalancer.URI % self.edge_id), condition=lambda ret: ret.loadBalancer.virtualServer is None, progress_cb=lambda x: "Waiting to remove virtual server on nsx edge, id={0}".format(self.edge_id), timeout=20, interval=4, timeout_message="Virtual Server was not removed on edge, id={0}".format(self.edge_id)) LOG.debug('Removed the virtual server = {0} from NSX'.format(self.virtual_server_id))
def setup(self): def wait_on_remove(lb_resp): return lb_resp.loadBalancer.pool == None LOG.debug('Removing the pool = {0} from NSX'.format(self.pool_name)) try: self.nsx_rst_api.put(LoadBalancer.URI % self.edge_id, payload=self.pay_load) except NetxResourceError: pass wait(lambda: self.nsx_rst_api.get(LoadBalancer.URI % self.edge_id), condition=lambda ret: ret.loadBalancer.pool is None, progress_cb=lambda x: "Waiting to remove pool nsx edge, id={0}".format(self.edge_id), timeout=20, interval=4, timeout_message="Pool was not removed on edge, id={0}".format(self.edge_id))
def teardown(self): def callback(): ret = AttrDict() self._post_stats = AttrDict() for device in self.devices: with SSHInterface(device=device) as ifc: ssh = ifc.api self._post_stats[device] = ssh.stat(self.filename) size_before = self._pre_stats[device].st_size size_after = self._post_stats[device].st_size delta = size_after - size_before LOG.debug('delta: %d', delta) resp = ssh.run('tail --bytes={0} {1}'.format(delta, self.filename)) ret[device] = resp.stdout return self.testcb(ret, self._post_stats) if self.timeout: return wait(callback, timeout=self.timeout) else: return callback()
def do_config_sync(self): groups = self.groups.keys() self.do_wait_valid_state() # If so_config_all() is called, use cas[0] device, otherwise sync using # first active device. if self.sync_device: first_active_device = self.sync_device else: # Wait for at least one Active device. active_devices = wait(self.do_get_active, timeout=30, interval=2) # Will initiate config sync only from the first Active device. first_active_device = active_devices[0] LOG.info("Doing Config Sync to group %s...", groups) cred = first_active_device[0].get_admin_creds() with IcontrolInterface(address=first_active_device[0].address, username=cred.username, password=cred.password, port=first_active_device[0].ports['https']) as icifc: ic = icifc.api # This device group appears starting in 11.6.0. Sync this as well. dgs = ic.Management.DeviceGroup.get_list() if ASM_DG in dgs: LOG.info("This appears to be BIGIP11.6.0+ because of %s...", ASM_DG) groups.append(ASM_DG) for dg in groups: ic.System.ConfigSync.synchronize_to_group(group=dg)
def self(self): LOG.debug("Delete NSX service manager id = {0}".format( self.manager_id)) try: self.nsx_rst_api.delete(ServiceManagers.ITEM_URI % self.manager_id) except NetxResourceError: pass wait( lambda x: self.nsx_rst_api.get(ServiceManagers.URI), condition=lambda ret: self.manager_id in [ sm.objectId for sm in ret.serviceManagers.serviceManager if sm.objectId == self.manager_id ], progress_cb=lambda x: "Waiting on service manager deletion, id={0}" .format(self.manager_id), timeout=20, interval=4, timeout_message="service manager, id={0} was not removed".format( self.manager_id))
def setup(self): LOG.debug('Removing the virtual server = {0} from NSX'.format( self.virtual_server_id)) try: self.nsx_rst_api.put(LoadBalancer.URI % self.edge_id, payload=self.pay_load) except NetxResourceError: pass wait(lambda: self.nsx_rst_api.get(LoadBalancer.URI % self.edge_id), condition=lambda ret: ret.loadBalancer.virtualServer is None, progress_cb=lambda x: "Waiting to remove virtual server on nsx edge, id={0}".format( self.edge_id), timeout=20, interval=4, timeout_message="Virtual Server was not removed on edge, id={0}". format(self.edge_id)) LOG.debug('Removed the virtual server = {0} from NSX'.format( self.virtual_server_id))
def do_wait_valid_state(self): LOG.info('Waiting until BIG-IPs are out of Disconnected state...') def get_sync_statuses(): ret = [] for device in self.cas + self.peers: device_cred = device.get_admin_creds() with EmapiInterface(username=device_cred.username, password=device_cred.password, port=device.ports['https'], address=device.address) as rstifc: api = rstifc.api entries = api.get(SyncStatus.URI)['entries'] x = [ entries[entry].nestedStats.entries.status.description for entry in entries ] ret.extend(x) return ret # Verify all BIG-IPs are at least 11.5.0 valid_bigips = True for device in self.cas + self.peers: device_cred = device.get_admin_creds() with IcontrolInterface(address=device.address, username=device_cred.username, password=device_cred.password, port=device.ports['https']) as icifc: v = icifc.version if v.product.is_bigip and v < 'bigip 11.5.0': valid_bigips = False if valid_bigips: wait(get_sync_statuses, condition=lambda ret: 'Disconnected' not in ret, progress_cb=lambda ret: "Device sync-status: {0}".format(ret), timeout=10) else: LOG.info( 'There are BIG-IPs that are older than 11.5.0. Skipping wait...' )
def setup(self): for virtual in self.api.get(WorkingLtmVip.URI)["items"]: if self.match == 'exact': if self.name == virtual.name: self.virtuals.append(virtual.name) self.api.delete(virtual.selfLink) elif self.match == 'contains': if self.name in virtual.name: self.virtuals.append(virtual.name) self.api.delete(virtual.selfLink) elif self.match == 'startswith': if virtual.name.startswith(self.name): self.virtuals.append(virtual.name) self.api.delete(virtual.selfLink) elif self.match == 'endswith': if virtual.name.endswith(self.name): self.virtuals.append(virtual.name) self.api.delete(virtual.selfLink) LOG.info("Ensure virtual servers no longer exist in ASM") wait(self.poll_virtuals_empty, interval=2, timeout=10, timeout_message="Virtual servers were not removed from bigiq in {0}s.")
def do_set_active(self): if isinstance(self.options.set_active, DeviceAccess): desired_active = self.options.set_active.address else: desired_active = self.options.set_active active_devices = wait(self.do_get_active, timeout=30, interval=2) device_map = {} for device in active_devices: if not device[0]: LOG.warning('No configuration found for device %s', device[2]) continue cred = device[0].get_admin_creds() with IcontrolInterface(address=device[0].address, username=cred.username, password=cred.password, port=device[0].ports['https']) as icifc: v = icifc.version if v.product.is_bigip and v < 'bigip 11.2.0' or \ v.product.is_em and v < 'em 3.0.0': LOG.warning('Set active not supported on this version (%s).', v) ic = icifc.api devices = ic.Management.Device.get_list() mgmtaddrs = ic.Management.Device.get_management_address(devices=devices) device_map = dict(zip(mgmtaddrs, devices)) if device_map.get(desired_active): LOG.info("Current Active device is %s.", device[2]) LOG.info("Setting %s to Active...", device_map[desired_active]) ic.System.Failover.set_standby_to_device(device=device_map[desired_active]) def _is_desired_device_active(devices): return [x for x in devices if x[1] == 'HA_STATE_ACTIVE' and x[2] == device_map[desired_active]] if device_map.get(desired_active): LOG.info("Waiting for Active status...") wait(self.do_get_active, _is_desired_device_active, timeout=10, interval=1)
def wait_for_webel(xpath=None, did=None, css=None, negated=False, timeout=10, interval=1, usedin=None, *args, **kwargs): """Wait for an element or a suite of elements (defined by xpath/css/did) to appear or disappear. @param xpath, css, did: At least one Mandatory. Defaulting to xpath. takes an xpath, css or an webel id to click on @type xpath, css, did: str @type negated: bool @param negated: to wait for the element(s) to not be there anymore """ usedin = "{0}/waitforwebel/".format(usedin if usedin else "") prop = ['text', 'is_displayed'] def to_be(): x = webel_grab(xpath=xpath, did=did, css=css, prop=prop, *args, **kwargs) return (x == [] and negated) or (x and not negated) wait(to_be, interval=interval, timeout=timeout, progress_cb=lambda x: '{0}Still looking for els...'.format(usedin), timeout_message="%sDid Not Find els in {0}s" % (usedin)) LOG.debug("{0}Waited for elements in [{1}]{2}...".format( usedin, xpath or css or did, "/negated" if negated else ""))
def device_rediscovery(self, device, biq_rstifc): # Remove the BIG-IP # Proceed when RMA fails in case device not DMA properly from previous test try: RCMD.device.delete_asm([device]) except: LOG.debug("Unexpected RMA error: %s" % sys.exc_info()[0]) # Ensure device is deleted from cm-asm-allAsmDevices group bip_rstifc = self.get_icontrol_rest(device=device).api resp = bip_rstifc.get(URL_TM_DEVICE_INFO) machineId = resp["machineId"] hostname = resp["hostname"] def query(): resp = biq_rstifc.get(CmAsmAllAsmDevicesGroup.URI) return machineId not in [item.machineId for item in resp["items"]] wait(query, interval=1, timeout=180, timeout_message="BIGIP %s didn't get removed from asm device group 180s after RMA." % hostname) # Discover the BIG-IP. ret = RCMD.device.discover_asm([device], timeout=540) return ret
def setup(self): self.x = [] def to_be(): self.x = webel_grab(xpath=self.xpath, did=self.did, css=self.css, prop=self.prop, ifc=self.ifc) return (self.x == [] and self.negated) or (self.x and not self.negated) wait(to_be, interval=self.interval, timeout=self.timeout, progress_cb=lambda x: '{0}Still looking for els...'.format( self.usedin), timeout_message="%sDid Not Find els in {0}s" % (self.usedin)) LOG.debug("{0}Waited for elements in [{1}]{2}...".format( self.usedin, self.xpath or self.css or self.did, "/negated" if self.negated else "")) return self.x
def setup(self): super(HAPromoteStage, self).setup() LOG.info('Promotion stage for: %s', self.default) self.default.specs.default = False self.peer.specs.default = True LOG.info("old default = %s", self.default) LOG.info("new default = %s", self.peer) # if this is active/standby, promote, otherwise, not needed. if self.ha_passive: # Prepare command to send to promote payload = Options() payload.command = 'SET_PRIMARY' LOG.info("Picking up the list of peers from the new primary") context = ContextHelper(__name__) rest = context.get_icontrol_rest(device=self.peer).api resp = rest.get(DeviceResolver.DEVICES_URI % DEFAULT_ALLBIGIQS_GROUP) # Look for the machine id of the peer to promote for item in resp['items']: if item.address == self.peer.get_discover_address(): payload.machineId = item.machineId LOG.info("Promoting peer to primary from peer") rest.post(FailoverState.URI, payload=payload) # wait for restjavad to go down... wait(lambda: rest.get(DeviceResolver.DEVICES_URI % DEFAULT_ALLBIGIQS_GROUP)['items'], negated=True, progress_cb=lambda ret: 'Waiting for restjavad on {0} to go down.'.format(self.default )) # wait for it to come back up RCMD.system.wait_restjavad([self.peer])
def do_BZ364939(self): devices = self.cas + self.peers devices.reverse() for device in devices: cred = device.get_admin_creds() with IcontrolInterface(address=device.address, username=cred.username, password=cred.password, port=device.ports['https']) as icifc: v = icifc.version if v.product.is_bigip and v >= 'bigip 11.2.0' or \ v.product.is_em and v >= 'em 3.0.0' or \ v.product.is_bigiq: continue # if icifc.version.product.is_bigip and icifc.version < 'bigip 11.2': ic = icifc.api LOG.info('Working around BZ364939 on %s...', device.alias) ic.System.Services.set_service(services=['SERVICE_TMM'], service_action='SERVICE_ACTION_RESTART') wait(ic.System.Failover.get_failover_state, lambda x: x != 'FAILOVER_STATE_OFFLINE')
def do_wait_valid_state(self): LOG.info('Waiting until BIG-IPs are out of Disconnected state...') def get_sync_statuses(): ret = [] for device in self.cas + self.peers: device_cred = device.get_admin_creds() with EmapiInterface(username=device_cred.username, password=device_cred.password, port=device.ports['https'], address=device.address) as rstifc: api = rstifc.api entries = api.get(SyncStatus.URI)['entries'] x = [entries[entry].nestedStats.entries.status.description for entry in entries] ret.extend(x) return ret # Verify all BIG-IPs are at least 11.5.0 valid_bigips = True for device in self.cas + self.peers: device_cred = device.get_admin_creds() with IcontrolInterface(address=device.address, username=device_cred.username, password=device_cred.password, port=device.ports['https']) as icifc: v = icifc.version if v.product.is_bigip and v < 'bigip 11.5.0': valid_bigips = False if valid_bigips: wait(get_sync_statuses, condition=lambda ret: 'Disconnected' not in ret, progress_cb=lambda ret: "Device sync-status: {0}".format(ret), timeout=10) else: LOG.info('There are BIG-IPs that are older than 11.5.0. Skipping wait...')
def teardown(self): ssh = self.ifc.api def callback(): self._post_stats = ssh.stat(self.filename) size_before = self._pre_stats.st_size size_after = self._post_stats.st_size delta = size_after - size_before LOG.debug('delta: %d', delta) ret = ssh.run('tail --bytes={0} {1}'.format(delta, self.filename)) return self.testcb(ret.stdout, self._post_stats) if self.timeout: return wait(callback, timeout=self.timeout, timeout_message=self.timeout_message) else: return callback()
def teardown(self): ssh = self.ifc.api def callback(): self._post_stats = ssh.stat(self.filename) size_before = self._pre_stats.st_size size_after = self._post_stats.st_size delta = size_after - size_before LOG.debug('delta: %d', delta) ret = ssh.run('tail --bytes={0} {1}'.format(delta, self.filename)) return self.testcb(ret.stdout, self._post_stats) if self.timeout: return wait(callback, timeout=self.timeout) else: return callback()
def open_file_safe(filepath, timeout=30): '''Workaround for BZ585092. Check if file can be opened before returning the file object. Args: filepath (str): The path of the file to be opened Returns: file: The file object from f.open() ''' def try_open(): f = open(filepath) LOG.info("File at %s opened." % filepath) return f f = wait(try_open, timeout=timeout, timeout_message="File not found after {0}s.") return f
if negated: return negated else: pass except StaleElementReferenceException: if negated: return negated else: pass except Exception, e: LOG.debug("{0}Error:".format(usedin)) raise e wait(to_appear, interval=interval, timeout=timeout, progress_cb=lambda x: '{0}Still looking for text "{1}"{2}...'.format(usedin, text, " (negated)" if negated else ""), timeout_message="%s. '%s %s%s' in {0}s" % (usedin, "Did Not Find text" if not negated else "Still Found", text, "[negated]" if negated else "")) return x webel_click = None class WebelClick(SeleniumCommand): # @IgnorePep8 """Clicks on given xpath or css or did - unless turned off, it will retry until click or timeout - ability to right click - ability to force click (js) - with the ability to wait for other elements to appear or disappear (negated) after the fact: ids, xpaths, css (list of ids are supported if the given var returns lists)
def setup(self): ic = self.api return wait(lambda: ic.Networking.VLAN.get_list(), condition=lambda x: self.name not in x, progress_cb=lambda x: 'Waiting for VLAN: {} to be removed from: {}'.format(self.name, x), timeout=self.timeout, interval=2)
def setup(self): LOG.info('Waiting for profile to equal {}'.format(self.state)) return wait(lambda: _get_vip_fb_persistence_profile(self.api, self.partition, self.vip_name), condition=lambda x: x == self.state, progress_cb=lambda x: 'Fallback Persistence Profile: {}'.format(x), timeout=20, interval=1)
def setup(self): return wait(lambda: _get_vip_connection_mirror_state(self.api, self.partition, self.vip_name), condition=lambda x: x == self.state, progress_cb=lambda x: 'Connection Mirroring: {}'.format(x), timeout=20, interval=1)
def setup(self): return wait(lambda: _get_vip_source_port_behavior(self.api, self.partition, self.vip_name), condition=lambda x: x == self.behavior, progress_cb=lambda x: 'Port Behavior: {}'.format(x), timeout=20, interval=1)
def complete_recover_graphs(self, original_xmls, apicifc, bigips): ''' Delete all graphs and Device Clusters Create Device Cluster and Graphs ''' try: LOG.info("Deleting graphs and device clusters...") for graph, ldevvip in reversed(original_xmls): vdev = ACMD.system.get_vdev(Tenant.TENANT_DN % graph.get('name'), ifc=self.apicifc) self.apicifc.api.delete(ManagedObject.URI % graph.get('dn')) if vdev is not None: partition_number = vdev.get('id') ctx_name = vdev.get('ctxName') tenant_name = graph.get('name') ip_partition_name = "apic-{0}-{1}-{2}".format( tenant_name, ctx_name, partition_number) LOG.info("Waiting until partition is removed from BIG-IPs:" " %s" % ip_partition_name) for bigip in self.get_data('devices'): r = self.get_icontrol_rest(device=bigip).api wait_args(r.get, func_args=[Folders.URI], condition=lambda x: ip_partition_name not in [item.name for item in x['items']], progress_cb=lambda x: "{0}".format( [item.name for item in x['items']])) if ldevvip is not None: self.apicifc.api.delete(ManagedObject.URI % ldevvip.get('dn')) ldevvip_name = ldevvip.get('name') LOG.info( "Waiting until Device Group gets removed from BIG-IPs: " "{0}".format(ldevvip_name)) for bigip in bigips: rstifc = self.get_icontrol_rest(device=bigip) wait(lambda: rstifc.api.get(DeviceGroup.URI), condition=lambda x: ldevvip_name not in [y.name for y in x['items']], progress_cb=lambda x: "Device Groups: {0}".format( [y.name for y in x['items']]), stabilize=5) LOG.info("Re-deploying graphs...") for graph, ldevvip in original_xmls: if ldevvip is not None: substrs = ldevvip.get('dn').split('/') for substr in substrs: if 'tn-' in substr: ldevvip_tenant = "uni/{}".format(substr) self.apicifc.api.post(ManagedObject.URI % ldevvip.get('dn'), payload=ldevvip) ldevvip.wait(ifc=apicifc, tenant=ldevvip_tenant) self.apicifc.api.post(ManagedObject.URI % graph.get('dn'), payload=graph) graph.wait_graph(self.apicifc, graph.get('name')) except: raise
class WebelClick(SeleniumCommand): # @IgnorePep8 """Clicks on given xpath or css or did - unless turned off, it will retry until click or timeout - ability to right click - ability to force click (js) - with the ability to wait for other elements to appear or disappear (negated) after the fact: ids, xpaths, css (list of ids are supported if the given var returns lists) - with the ability to wait for a text in a webel or even in webel attributes to appear or disappear (negated) after the fact - it will wait for all of the above one after the other @param xpath, css, did: At least one Mandatory. defaulting to xpath. takes an xpath, css or an webel id to click on @type xpath, css, did: str Optional Parameters: @param retryit: default True, to retry in case the element is not there yet @type retryit: bool @param waitforxpath, waitforid, waitforcss: takes an xpath to wait for it to become visible after the fact @type waitforxpath, waitforid, waitforcss: str @param waitfortext: text to wait for in a different web element(s) or attr/prop of such @type waitfortext: str @param attr: list of attributes/props of an web element where a text can be searched within @type attr: list of strings, eg: ['value', 'class'] @param inxpath, incss, indid: the webel identifier where to look for the text @type inxpath, incss, indid: str @type negated,jsclick: bool @param negated: to wait for the element to not be there anymore (after the fact only) @param jsclick: jsclick (even if invisible) (force). Default False. @param timeout: how long to wait for each wait action @type timeout: int @param right_click: default False, to right click on that el. @type right_click: bool @param double_click: default False, to double click on that el. @type double_click: bool @return: webel pos in browser if found and clicked, or fail """ def __init__(self, xpath=None, css=None, did=None, waitforid=None, waitforxpath=None, waitforcss=None, waitfortext=None, inxpath=None, incss=None, indid=None, attr=None, mtm=False, negated=False, right_click=False, double_click=False, jsclick=False, retryit=True, timeout=5, *args, **kwargs): super(WebelClick, self).__init__(*args, **kwargs) if (waitfortext and not (inxpath or incss or indid) or not waitfortext and (inxpath or incss or indid)): raise WrongParameterPassedMethodCheck("waitfortext parameter must" " be used with one of " "inxpath/incss/indid...") self.xpath = xpath self.css = css self.did = did self.waitforid = waitforid self.waitforxpath = waitforxpath self.waitforcss = waitforcss self.waitfortext = waitfortext self.inxpath = inxpath self.incss = incss self.indid = indid self.mtm = mtm self.negated = negated self.timeout = timeout self.jsclick = jsclick if attr is None: attr = [] self.attr = attr self.retryit = retryit self.right_click = right_click self.double_click = double_click def setup(self): # To Do: Validate xpath/css/did el self.using = 'xpath' self.s = None def retrythis(): button = None isit = False try: LOG.debug("/WebelClick/{0}/Fetching:'{1}'".format( self.using, self.xpath or self.css or self.did)) if self.xpath: button = self.api.find_element_by_xpath(self.xpath) elif self.css: self.using = 'css' button = self.api.find_element_by_css_selector(self.css) elif self.did: self.using = 'id' button = self.api.find_element_by_id(self.did) if button: if self.jsclick and not self.right_click: self.s = self.api.execute_script( "return arguments[0].click()", button) elif not self.jsclick and self.right_click: LOG.debug( "/WebelClick/{0}/Entering Action Chains.".format( self.using)) action = ActionChains(self.api) # LOG.debug("/WebelClick/{0}/In Action Chains: Move to El.".format(self.using)) action.move_to_element(button) # LOG.debug("/WebelClick/{0}/In Action Chains: Stabilizing 1 sec.".format(self.using)) # time.sleep(1) # LOG.debug("/WebelClick/{0}/In Action Chains: Context Click.".format(self.using)) action.context_click() # LOG.debug("/WebelClick/{0}/In Action Chains: Perform().".format(self.using)) self.s = action.perform() LOG.debug( "/WebelClick/{0}/After Action Chains: Perform(): Finished." .format(self.using)) elif not self.jsclick and not self.right_click and self.double_click: LOG.debug( "/WebelClick/{0}/Entering Action Chains.".format( self.using)) action = ActionChains(self.api) action.double_click(button) self.s = action.perform() LOG.debug( "/WebelClick/{0}/After Action Chains: Perform(): Finished." .format(self.using)) elif not self.jsclick and not self.right_click: self.s = button.click() else: # force js right click # NOT IMPLEMENTED YET # not sure is needed # self.s = self.api.execute_script("return arguments[0].context_click()", # button) LOG.error( "/WebelClick/Can't force right click on invisible element yet." ) pass isit = True except NoSuchElementException: isit = False LOG.debug( "/WebelClick/{0}/except: NoSuchElementException. Passing.". format(self.using)) pass except ElementNotVisibleException: isit = False LOG.warning( "/WebelClick/{0}/'{1}'/ElementNotVisibleException! Passing." .format(self.using, self.xpath or self.css or self.did)) pass except Exception, e: raise e return isit if self.retryit: wait(retrythis, interval=1, timeout=self.timeout, progress_cb=lambda x: "/WebelClick/{0}/'{1}'/Retry Click...". format(self.using, self.xpath or self.css or self.did), timeout_message="/WebelClick/%s/'%s'/Could not Click " "it after {0}s" % (self.using, (self.xpath or self.css or self.did))) else: retrythis() if self.waitforid: self.s = self.api self.s = self.s.wait(self.waitforid, negated=self.negated, timeout=self.timeout) if self.waitforxpath: self.s = self.api self.s = self.s.wait(self.waitforxpath, By.XPATH, negated=self.negated, timeout=self.timeout) if self.waitforcss: self.s = self.api self.s = self.s.wait(self.waitforcss, By.CSS_SELECTOR, negated=self.negated, timeout=self.timeout) if self.waitfortext and (self.inxpath or self.incss or self.indid): wait_for_text_in_webel(text=self.waitfortext, xpath=self.inxpath, css=self.incss, did=self.indid, attr=self.attr, mtm=self.mtm, negated=self.negated, timeout=self.timeout, ifc=self.ifc) return self.s
def wait_for_text_in_webel(text, xpath=None, did=None, css=None, mtm=False, attr=None, textineach=False, visibleonly=False, prop=None, negated=False, timeout=10, usedin=None, interval=1, *args, **kwargs): """Wait for a text (or multiple text) to appear within a list of elements @param text: Mandatory. String to search for. -In the special case where you would like to see if a multi-element xpath returns at least one visible element, you can pass an empty text with visibleonly=True -If regex_transform is used, it will break it down based on normal separators and will try to match substrings one after the other. @type text: string @param xpath, css, did: At least one Mandatory. defaulting to xpath. takes an xpath, css or an webel id to click on @type xpath, css, did: str Optional Parameters: @param mtm: default False. Multiple "AND" Text Only Match. Will break down the string into substrings so it can match all of them (one after the other - lookahead assertion) into the received el(s) list of attributes/props Keeps text and numbers only, but also keeps: - single spaces; - "-" - "_" For Multi-match, in between desired texts, use special chars like: - "*" or - "\" or - ":" (etc.) @type mtm: bool @param attr: Default None. Look into received specific attributes. E.g. ['class', 'style'] @type attr: list of strings @param prop: Default: ['text', 'is_displayed']. 1. Will look into the element text. One should not remove this prop. 2. Will look to see if the element is displayed. (remove this and will not look for True on this property) @type prop: list of strings @param negated: default False. Inverts action. Makes sure the text is not in the el @type negated: bool @param visibleonly: default False. It searches only in visible elements. @type negated: bool @param timeout: default 10 seconds. It will retry the search for 10 seconds. Does not matter if used in conjunction with negated=True @type timeout: int @return: Returns the generated list of the search if found or fail. If negated, returns True or False""" usedin = "{0}/waitfortext/".format(usedin if usedin else "") x = [] if not prop and not attr: prop = ['text', 'is_displayed'] if not prop: prop = [] if not attr: attr = [] if visibleonly and 'is_displayed' not in prop: prop.append('is_displayed') ftext = None if mtm: # Matches one or more non-alphanumeric characters or "_" or space: # ([^\w\s]|_)- followed by either a space or the end of string ($). # The (?= ) construct is a lookahead assertion: # it makes sure that a matching space is not included in the match, # so it doesn't get replaced; only the [\W_]+ gets replaced. # Then all (such) or new line chars\specials are replaced with ".*?" # Use "*" or "\" etc. for multi text assertion. ftext = text.encode('utf-8') try: ftext = re.sub( r'([^\w\s]|_)+(?=\s|$)|(\n|\(|\)|\+|\[|\]|\{|\}|\\|\/|\*)', \ ".*?", ftext) except Exception as e: LOG.debug("{0}Error in re.sub:".format(usedin)) raise e while ftext[-3:] == ".*?": ftext = ftext[:-3] LOG.debug("{0}text: '{1}'/Formed regex text is: '{2}'".format( usedin, text, ftext)) def to_appear(): try: x = webel_grab(xpath=xpath, did=did, css=css, attr=attr, prop=prop, *args, **kwargs) LOG.debug("{0}. Looking for '{1}'; Grabbed: '{2}'".format( usedin, text, x)) if negated and x == []: return True occured = False if x != [] and 'text' in prop: for el in x: if textineach: occured = False ret = None # look in text properties eltext = el.text if mtm: # if using multi text match (regex lookahead assert) eltext = eltext.encode('utf-8') ret = re.search(ftext, eltext, re.S | re.U) LOG.debug("{0}current re.search result: {1}".format( usedin, ret)) # if looking for a visible element if 'is_displayed' in prop and ret: if el.is_displayed: LOG.debug( "{0}regex:'{1}'/. Found '{2}' in above Grabbed!" .format(usedin, ftext, text)) occured = True # if it does not matter if the element is visible or not elif ret: LOG.debug( "{0}regex:'{1}'/. Found '{2}' in above Grabbed!" .format(usedin, ftext, text)) occured = True else: # regular exact text match search (old style) # if looking for a visible element if 'is_displayed' in prop and text in eltext: if el.is_displayed: LOG.debug( "{0}exact match/Found '{1}' in in above Grabbed!" .format(usedin, text)) occured = True # if it does not matter if the element is visible or not elif text in eltext: LOG.debug( "{0}exact match/Found '{1}' in above Grabbed!". format(usedin, text)) occured = True # (also) look in attributes (if not found already in properties) if x != [] and attr != [] and not occured: for el in x: if (not visibleonly) or (visibleonly and el.get("is_displayed")): for val in attr: LOG.debug( "{0}looking in attr [{1}]; got: [{2}]".format( usedin, val, el.get(val))) # LOG.info("{0}looking in attr [{1}]; got: [{2}]" # .format(usedin, val, el.get(val))) if textineach: occured = False # if using multi text match (regex lookahead assert) if mtm: ret = None eltext = el.get(val) eltext = eltext.encode('utf-8') ret = re.search(ftext, eltext, re.S | re.U) LOG.debug( "{0}current re.search result: {1}".format( usedin, ret)) if ret: LOG.debug( "{0}regex:'{1}'/. Found '{2}' in above Grabbed!" .format(usedin, ftext, text)) occured = True else: if textineach: break # regular exact text match search (old style) else: if text in el.get(val): occured = True else: if textineach: break if textineach and not occured: break if occured and not negated: return True if not occured and negated: return True except NoSuchElementException: if negated: return negated else: pass except StaleElementReferenceException: if negated: return negated else: pass except Exception as e: LOG.debug("{0}Error:".format(usedin)) raise e wait(to_appear, interval=interval, timeout=timeout, progress_cb=lambda x: '{0}Still looking for text "{1}"{2}...'.format( usedin, text, " (negated)" if negated else ""), timeout_message="%s. '%s %s%s' in {0}s" % (usedin, "Did Not Find text" if not negated else "Still Found", text, "[negated]" if negated else "")) return x
def setup(self): # To Do: Validate xpath/css/did el self.using = 'xpath' self.s = None def retrythis(): button = None isit = False try: LOG.debug("/WebelClick/{0}/Fetching:'{1}'".format( self.using, self.xpath or self.css or self.did)) if self.xpath: button = self.api.find_element_by_xpath(self.xpath) elif self.css: self.using = 'css' button = self.api.find_element_by_css_selector(self.css) elif self.did: self.using = 'id' button = self.api.find_element_by_id(self.did) if button: if self.jsclick and not self.right_click: self.s = self.api.execute_script( "return arguments[0].click()", button) elif not self.jsclick and self.right_click: LOG.debug( "/WebelClick/{0}/Entering Action Chains.".format( self.using)) action = ActionChains(self.api) # LOG.debug("/WebelClick/{0}/In Action Chains: Move to El.".format(self.using)) action.move_to_element(button) # LOG.debug("/WebelClick/{0}/In Action Chains: Stabilizing 1 sec.".format(self.using)) # time.sleep(1) # LOG.debug("/WebelClick/{0}/In Action Chains: Context Click.".format(self.using)) action.context_click() # LOG.debug("/WebelClick/{0}/In Action Chains: Perform().".format(self.using)) self.s = action.perform() LOG.debug( "/WebelClick/{0}/After Action Chains: Perform(): Finished." .format(self.using)) elif not self.jsclick and not self.right_click and self.double_click: LOG.debug( "/WebelClick/{0}/Entering Action Chains.".format( self.using)) action = ActionChains(self.api) action.double_click(button) self.s = action.perform() LOG.debug( "/WebelClick/{0}/After Action Chains: Perform(): Finished." .format(self.using)) elif not self.jsclick and not self.right_click: self.s = button.click() else: # force js right click # NOT IMPLEMENTED YET # not sure is needed # self.s = self.api.execute_script("return arguments[0].context_click()", # button) LOG.error( "/WebelClick/Can't force right click on invisible element yet." ) pass isit = True except NoSuchElementException: isit = False LOG.debug( "/WebelClick/{0}/except: NoSuchElementException. Passing.". format(self.using)) pass except ElementNotVisibleException: isit = False LOG.warning( "/WebelClick/{0}/'{1}'/ElementNotVisibleException! Passing." .format(self.using, self.xpath or self.css or self.did)) pass except Exception as e: raise e return isit if self.retryit: wait(retrythis, interval=1, timeout=self.timeout, progress_cb=lambda x: "/WebelClick/{0}/'{1}'/Retry Click...". format(self.using, self.xpath or self.css or self.did), timeout_message="/WebelClick/%s/'%s'/Could not Click " "it after {0}s" % (self.using, (self.xpath or self.css or self.did))) else: retrythis() if self.waitforid: self.s = self.api self.s = self.s.wait(self.waitforid, negated=self.negated, timeout=self.timeout) if self.waitforxpath: self.s = self.api self.s = self.s.wait(self.waitforxpath, By.XPATH, negated=self.negated, timeout=self.timeout) if self.waitforcss: self.s = self.api self.s = self.s.wait(self.waitforcss, By.CSS_SELECTOR, negated=self.negated, timeout=self.timeout) if self.waitfortext and (self.inxpath or self.incss or self.indid): wait_for_text_in_webel(text=self.waitfortext, xpath=self.inxpath, css=self.incss, did=self.indid, attr=self.attr, mtm=self.mtm, negated=self.negated, timeout=self.timeout, ifc=self.ifc) return self.s
return negated else: pass except StaleElementReferenceException: if negated: return negated else: pass except Exception, e: LOG.debug("{0}Error:".format(usedin)) raise e wait(to_appear, interval=interval, timeout=timeout, progress_cb=lambda x: '{0}Still looking for text "{1}"{2}...'.format( usedin, text, " (negated)" if negated else ""), timeout_message="%s. '%s %s%s' in {0}s" % (usedin, "Did Not Find text" if not negated else "Still Found", text, "[negated]" if negated else "")) return x webel_click = None class WebelClick(SeleniumCommand): # @IgnorePep8 """Clicks on given xpath or css or did - unless turned off, it will retry until click or timeout - ability to right click - ability to force click (js) - with the ability to wait for other elements to appear or disappear (negated)