예제 #1
0
    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')
예제 #2
0
    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)
예제 #3
0
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)
예제 #4
0
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
예제 #5
0
 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
예제 #6
0
 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
예제 #7
0
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
예제 #8
0
 def __init__(self, provider: ValueProvider):
     self.provider = provider
     self.kv = KVAdapter()
예제 #9
0
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)
예제 #10
0
 def __init__(self, node: str) -> None:
     self.kv = KVAdapter()
     self.node = node
예제 #11
0
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]