def test_key_prefix_correct(self): kv = KVAdapter() kv.kv_put = MagicMock() storage = OffsetStorage('my-node' , key_prefix='bq-delivered', kv=kv) storage.mark_last_read(150) kv.kv_put.assert_called_with('bq-delivered/my-node', '150')
def test_key_prefix_used_everywhere(self): kv = KVAdapter() kv.kv_put = MagicMock() kv.kv_get = MagicMock(side_effect=[{'Value': '120'}]) storage = OffsetStorage('server1' , key_prefix='somequeue', kv=kv) storage.mark_last_read(120) epoch = storage.get_last_read_epoch() kv.kv_put.assert_called_with('somequeue/server1', '120') kv.kv_get.assert_called_with('somequeue/server1') self.assertEqual(120, epoch)
def all_services_started(url: str, nr_svcs: int) -> bool: conf = ConfStoreProvider(url) hostname = conf.get_hostname() kv = KVAdapter() status_data = kv.kv_get(f'{hostname}/processes', recurse=True) statuses = [] for val in status_data: state = val['Value'] statuses.append(json.loads(state.decode('utf8'))['state']) started = [status == 'M0_CONF_HA_PROCESS_STARTED' for status in statuses] if len(started) != nr_svcs: return False return all(started)
def kv_adapter(mocker): kv = KVAdapter() exc = RuntimeError('Not allowed') def fake_get(key): if key in [f'my-node-{i}/facts' for i in [1, 2, 3]]: return new_kv( key, json.dumps({ 'processorcount': 1, 'memorysize_mb': 1024.0 })) if key in [ f'my-node-{i}/dev/sd{x}' for i in [1, 2, 3] for x in ['b', 'c'] ]: return new_kv( key, json.dumps({ 'path': '/stub/', 'size': 102400, 'blksize': 8 })) raise RuntimeError(f'Unexpected key = {key}') mock = mocker.patch.object mock(kv, 'kv_get', side_effect=fake_get) mock(kv, 'kv_put', side_effect=exc) mock(kv, 'kv_put_in_transaction', side_effect=exc) mock(kv, 'kv_delete_in_transaction', side_effect=exc) return kv
def __init__(self, queue_prefix: str, kv: Optional[KVAdapter] = None, epoch_key: str = 'epoch'): self.queue_prefix = queue_prefix self.kv = kv or KVAdapter() self.epoch_key = epoch_key
def __init__(self, node_name: str, key_prefix: str = '', kv: Optional[KVAdapter] = None): self.node_name = node_name self.kv = kv or KVAdapter() self.key_prefix = key_prefix
def all_services_started(url: str, processes: Dict[str, List[Fid]]) -> bool: kv = KVAdapter() if not processes: return False for key in processes.keys(): for proc_fid in processes[key]: proc_state = kv.kv_get(f'{key}/processes/{proc_fid}', recurse=True, allow_null=True) if proc_state: proc_state_val = proc_state[0]['Value'] state = json.loads(proc_state_val.decode('utf8'))['state'] if state != 'M0_CONF_HA_PROCESS_STARTED': return False else: return False return True
def __init__(self, provider: ValueProvider): self.provider = provider self.kv = KVAdapter()
class Utils: def __init__(self, provider: ValueProvider): self.provider = provider self.kv = KVAdapter() @func_log(func_enter, func_leave) def get_hostname(self, machine_id: str) -> str: """ Returns the hostname of the given machine_id according to the given ConfStore (ValueProvider). """ store = self.provider hostname = store.get( f'node>{machine_id}>network>data>private_fqdn', allow_null=True) return hostname or store.get(f'node>{machine_id}>hostname') @func_log(func_enter, func_leave) def get_local_hostname(self) -> str: """ Retrieves the machine-id of the node where the code runs and fetches its hostname from the ConfStore (ValueProvider). """ store = self.provider machine_id = store.get_machine_id() return self.get_hostname(machine_id) @func_log(func_enter, func_leave) @repeat_if_fails() def save_node_facts(self): hostname = self.get_local_hostname() cmd = ['facter', '--json', 'processorcount', 'memorysize_mb'] node_facts = execute(cmd) self.kv.kv_put(f'{hostname}/facts', node_facts) @func_log(func_enter, func_leave) @repeat_if_fails() def get_node_facts(self): hostname = self.get_local_hostname() node_facts = None node_facts_val = None while (not node_facts or node_facts is None): try: node_facts = self.kv.kv_get(f'{hostname}/facts') node_facts_val = json.loads(node_facts['Value']) except TypeError: logging.info('%s facts not available yet, retrying...', hostname) sleep(2) continue return node_facts_val @func_log(func_enter, func_leave) def get_data_devices(self, machine_id: str, cvg: int) -> DList[Text]: data_devices = DList( [Text(device) for device in self.provider.get( f'node>{machine_id}>' f'storage>cvg[{cvg}]>devices>data')], 'List Text') return data_devices @func_log(func_enter, func_leave) def _get_drive_info_form_os(self, path: str) -> Disk: drive_size = 0 with open(path, 'rb') as f: drive_size = f.seek(0, io.SEEK_END) return Disk(path=Maybe(Text(path), 'Text'), size=Maybe(drive_size, 'Natural'), blksize=Maybe(os.stat(path).st_blksize, 'Natural')) @func_log(func_enter, func_leave) @repeat_if_fails() def _save_drive_info(self, path: str): disk: Disk = self._get_drive_info_form_os(path) hostname = self.get_local_hostname() drive_info = json.dumps({'path': disk.path.get().s, 'size': int(disk.size.get()), 'blksize': int(disk.blksize.get())}) disk_key = path.strip('/') self.kv.kv_put(f'{hostname}/drives/{disk_key}', drive_info) @func_log(func_enter, func_leave) @repeat_if_fails() def save_log_path(self): hostname = self.get_local_hostname() machine_id = self.provider.get_machine_id() log_key = self.provider.get('cortx>common>storage>log') log_path = log_key + LOG_DIR_EXT + machine_id self.kv.kv_put(f'{hostname}/log_path', log_path) @func_log(func_enter, func_leave) def is_motr_io_present(self, machine_id: str) -> bool: """ Returns True if motr component is present in the components list for the given node>{machine_id}. """ return self.is_component_and_service(machine_id, Const.COMPONENT_MOTR.value, Const.SERVICE_MOTR_IO.value) @func_log(func_enter, func_leave) def is_component(self, machine_id: str, name: str) -> bool: """ Returns True if the given component is present in the components list for the given node>{machine_id} according to the ConfStore (ValueProvider). """ comp_names = self.provider.get(f'node>{machine_id}>' f'components') found = False for component in comp_names: if(component.get('name') == name): found = True break return found @func_log(func_enter, func_leave) def is_component_and_service(self, machine_id: str, comp_name: str, svc_name: str) -> bool: """ Returns True if the given service and component is present in the components list for the given node>{machine_id} according to the ConfStore (ValueProvider). """ return self.is_component(machine_id, comp_name) and \ self.is_service(machine_id, svc_name) @func_log(func_enter, func_leave) def is_component_or_service(self, machine_id: str, name: str) -> bool: """ Returns True if the given service or component is present in the components list for the given node>{machine_id} according to the ConfStore (ValueProvider). """ return self.is_component(machine_id, name) or \ self.is_service(machine_id, name) @func_log(func_enter, func_leave) def is_service(self, machine_id: str, svc_name: str) -> bool: """ Returns True if the given service is present in the components list for the given node>{machine_id} according to the ConfStore (ValueProvider). """ comp_names = self.provider.get(f'node>{machine_id}>' f'components') found: bool = False for component in comp_names: svc_names = component.get('services') if svc_names: for service in svc_names: if(service == svc_name): found = True break return found @func_log(func_enter, func_leave) def save_drives_info(self): machine_id = self.provider.get_machine_id() if(self.is_motr_io_present(machine_id)): cvgs_key: str = f'node>{machine_id}>storage>cvg' for cvg in range(len(self.provider.get(cvgs_key))): data_devs = self.get_data_devices(machine_id, cvg) for dev_path in data_devs.value: self._save_drive_info(dev_path.s) @func_log(func_enter, func_leave) @repeat_if_fails() def get_drive_info_from_consul(self, path: Text, machine_id: str) -> Disk: hostname = self.get_hostname(machine_id) disk_path = json.loads(str(path)).lstrip(os.sep) drive_data = None drive_info = None while (not drive_data or drive_data is None): try: drive_data = self.kv.kv_get(f'{hostname}/drives/{disk_path}') drive_info = json.loads(drive_data['Value']) except TypeError: logging.info('%s details are not available yet, retrying...', disk_path) sleep(2) continue return (Disk(path=Maybe(path, 'Text'), size=Maybe(drive_info['size'], 'Natural'), blksize=Maybe(drive_info['blksize'], 'Natural'))) @func_log(func_enter, func_leave) def get_drives_info_for(self, cvg: int, machine_id: str) -> DList[Disk]: data_devs = self.get_data_devices(machine_id, cvg) return DList([self.get_drive_info_from_consul(dev_path, machine_id) for dev_path in data_devs.value], 'List Disk') @func_log(func_enter, func_leave) def import_kv(self, conf_dir_path: str): with open(f'{conf_dir_path}/consul-kv.json') as f: data = json.load(f) for item in data: item_data = json.loads(json.dumps(item)) self.kv.kv_put(item_data['key'], str(item_data['value'])) @func_log(func_enter, func_leave) def copy_conf_files(self, conf_dir_path: str): machine_id = self.provider.get_machine_id() global_config_path = self.provider.get('cortx>common>storage>local') dest_motr = f'{global_config_path}/motr/sysconfig/{machine_id}' os.makedirs(dest_motr, exist_ok=True) cmd = ['/opt/seagate/cortx/hare/libexec/node-name', '--conf-dir', conf_dir_path] node_name = execute(cmd) copy_tree(f'{conf_dir_path}/sysconfig/motr/{node_name}', dest_motr) with open(f'{conf_dir_path}/consul-kv.json') as f: data = json.load(f) for item in data: item_data = json.loads(json.dumps(item)) if item_data['key'] == 'm0_client_types': m0_client_types = item_data['value'] break for client_type in json.loads(m0_client_types): src = f'{conf_dir_path}/sysconfig/{client_type}/{node_name}' dest = f'{global_config_path}/{client_type}/sysconfig/{machine_id}' os.makedirs(dest, exist_ok=True) copy_tree(src, dest) shutil.copyfile( f'{conf_dir_path}/confd.xc', f'{dest_motr}/confd.xc') @func_log(func_enter, func_leave) def copy_consul_files(self, conf_dir_path: str, mode: str): shutil.copyfile( f'{conf_dir_path}/consul-{mode}-conf/consul-{mode}-conf.json', f'{conf_dir_path}/consul/config/consul-{mode}-conf.json') @func_log(func_enter, func_leave) def stop_hare(self): self.hare_stop = True @func_log(func_enter, func_leave) def is_hare_stopping(self) -> bool: return self.hare_stop @func_log(func_enter, func_leave) def get_transport_type(self) -> str: transport_type = self.provider.get( 'cortx>motr>transport_type', allow_null=True) if transport_type is None: return 'libfab' return transport_type @func_log(func_enter, func_leave) @repeat_if_fails() def save_config_path(self, path: str): self.kv.kv_put('config_path', path)
def __init__(self, node: str) -> None: self.kv = KVAdapter() self.node = node
class ConsulKV: """ Helper class to fetch data from consul kv. """ def __init__(self, node: str) -> None: self.kv = KVAdapter() self.node = node @repeat_if_fails() def get_service_ids(self, svc_name: str) -> List[str]: """ Returns the service id by its name assuming that it runs at the current node. Examples of the key that will match: m0conf/nodes/srvnode1/processes/6/services/ha """ node_items = self.kv.kv_get('m0conf/nodes', recurse=True) regex = re.compile(f'^m0conf/nodes/{self.node}\\/processes/([0-9]+)' f'/services/{svc_name}$') keys = [] for key in node_items: match_result = re.match(regex, key['Key']) if not match_result: continue keys.append(match_result.group(1)) return keys @repeat_if_fails() def get_service_ep(self, proc_id: str) -> Optional[str]: """ Returns the service endpoint from the given process id assuming that it runs at the current node. Examples of the key that will match: key = m0conf/nodes/srvnode-1/processes/10/endpoint value = inet:tcp:10.230.246.59@3001 """ key = self.kv.kv_get( f'm0conf/nodes/{self.node}/processes/{proc_id}/endpoint') if key: return key['Value'].decode("utf-8") return None @repeat_if_fails() def get_ios_meta_data(self, proc_id: str) -> Optional[str]: """ Returns the metadata path for a given ioservice fid. Examples of the key that will match: key = m0conf/nodes/srvnode-1.data.private/processes/10/meta_data value = /dev/vg_srvnode-1_md1/lv_raw_md1 """ key = self.kv.kv_get( f'm0conf/nodes/{self.node}/processes/{proc_id}/meta_data') if key: return key['Value'].decode("utf-8") return None @repeat_if_fails() def get_profile_fid(self) -> str: profile_key = self.kv.kv_get('m0conf/profiles/', keys=True) assert profile_key # Get the first profile fid return profile_key[0].split('/')[-1]