def __getattr__(self, item): from ovs_extensions.generic.toolbox import ExtensionsToolbox if item.startswith('configure_'): section = ExtensionsToolbox.remove_prefix(item, 'configure_') return lambda **kwargs: self._add(section, **kwargs) if item.startswith('clear_'): section = ExtensionsToolbox.remove_prefix(item, 'clear_') return lambda: self._delete(section)
def list(self, key, recursive=False): # type: (str, bool) -> Generator[str] """ List all keys starting with specified key :param key: Key to list :type key: str :param recursive: List keys recursively :type recursive: bool :return: Generator with all keys :rtype: generator """ key = self._clean_key(key) entries = [] for entry in self._client.prefix(key): if entry.startswith('_'): continue if recursive is True: parts = entry.split('/') for index, part in enumerate(parts): if index == len(parts) - 1: # Last part yield entry # Every entry is unique, so when having reached last part, we yield it else: dir_name = '{0}/'.format('/'.join(parts[:index + 1])) if dir_name not in entries: entries.append(dir_name) yield dir_name else: if key == '' or entry.startswith(key.rstrip('/') + '/'): cleaned = ExtensionsToolbox.remove_prefix( entry, key).strip('/').split('/')[0] if cleaned not in entries: entries.append(cleaned) yield cleaned
def regenerate_service(self, name, client, target_name): # type: (str, SSHClient, str) -> None """ Regenerates the service files of a service. :param name: Template name of the service to regenerate :type name: str :param client: Client on which to regenerate the service :type client: ovs_extensions.generic.sshclient.SSHClient :param target_name: The current service name eg ovs-volumedriver_flash01.service :type target_name: str :return: None :rtype: NoneType :raises: RuntimeError if the regeneration failed """ configuration_key = self.service_config_key.format(self._system.get_my_machine_id(client), ExtensionsToolbox.remove_prefix(target_name, self.OVS_SERVICE_PREFIX)) # If the entry is stored in arakoon, it means the service file was previously made if not self._configuration.exists(configuration_key): raise RuntimeError('Service {0} was not previously added and cannot be regenerated.'.format(target_name)) # Rewrite the service file service_params = self._configuration.get(configuration_key) startup_dependency = service_params['STARTUP_DEPENDENCY'] if startup_dependency == '': startup_dependency = None else: startup_dependency = '.'.join(startup_dependency.split('.')[:-1]) # Remove .service from startup dependency output = self.add_service(name=name, client=client, params=service_params, target_name=target_name, startup_dependency=startup_dependency, delay_registration=True) if output is None: raise RuntimeError('Regenerating files for service {0} has failed'.format(target_name))
def add_service(self, name, client, params=None, target_name=None, startup_dependency=None, delay_registration=False, path=None): # type: (str, SSHClient, dict, str, str, bool, str) -> dict """ Add a service :param name: Template name of the service to add :type name: str :param client: Client on which to add the service :type client: ovs_extensions.generic.sshclient.SSHClient :param params: Additional information about the service :type params: dict or None :param target_name: Overrule default name of the service with this name :type target_name: str or None :param startup_dependency: Additional startup dependency :type startup_dependency: str or None :param delay_registration: Register the service parameters in the config management right away or not :type delay_registration: bool :param path: path to add service to :type path: str :return: Parameters used by the service :rtype: dict """ if params is None: params = {} if path is None: path = self._config_template_dir.format('systemd') else: path = path.format('systemd') service_name = self._get_name(name, client, path) template_file = '{0}/{1}.service'.format(path, service_name) if not client.file_exists(template_file): # Given template doesn't exist so we are probably using system init scripts return {} if target_name is not None: service_name = target_name params.update({'SERVICE_NAME': ExtensionsToolbox.remove_prefix(service_name, 'ovs-'), 'RUN_FILE_DIR': self._run_file_dir, 'STARTUP_DEPENDENCY': '' if startup_dependency is None else '{0}.service'.format(startup_dependency)}) template_content = client.file_read(template_file) for key, value in params.iteritems(): template_content = template_content.replace('<{0}>'.format(key), str(value)) service_path = self.get_service_file_path(service_name) client.file_write(service_path, template_content) try: client.run(['systemctl', 'daemon-reload']) client.run(['systemctl', 'enable', '{0}.service'.format(service_name)]) except CalledProcessError as cpe: self._logger.exception('Add {0}.service failed, {1}'.format(service_name, cpe.output)) raise Exception('Add {0}.service failed, {1}'.format(service_name, cpe.output)) if delay_registration is False: self.register_service(service_metadata=params, node_name=self._system.get_my_machine_id(client)) return params
def collapse_arakoon(): """ Collapse Arakoon's Tlogs :return: None """ from ovs_extensions.generic.toolbox import ExtensionsToolbox GenericController._logger.info('Arakoon collapse started') cluster_info = [] storagerouters = StorageRouterList.get_storagerouters() if os.environ.get('RUNNING_UNITTESTS') != 'True': cluster_info = [('cacc', storagerouters[0])] cluster_names = [] for service in ServiceList.get_services(): if service.is_internal is True and service.type.name in (ServiceType.SERVICE_TYPES.ARAKOON, ServiceType.SERVICE_TYPES.NS_MGR, ServiceType.SERVICE_TYPES.ALBA_MGR): cluster = ExtensionsToolbox.remove_prefix(service.name, 'arakoon-') if cluster in cluster_names and cluster not in [ARAKOON_NAME, ARAKOON_NAME_UNITTEST]: continue cluster_names.append(cluster) cluster_info.append((cluster, service.storagerouter)) workload = {} cluster_config_map = {} for cluster, storagerouter in cluster_info: GenericController._logger.debug(' Collecting info for cluster {0}'.format(cluster)) ip = storagerouter.ip if cluster in [ARAKOON_NAME, ARAKOON_NAME_UNITTEST] else None try: config = ArakoonClusterConfig(cluster_id=cluster, source_ip=ip) cluster_config_map[cluster] = config except: GenericController._logger.exception(' Retrieving cluster information on {0} for {1} failed'.format(storagerouter.ip, cluster)) continue for node in config.nodes: if node.ip not in workload: workload[node.ip] = {'node_id': node.name, 'clusters': []} workload[node.ip]['clusters'].append((cluster, ip)) for storagerouter in storagerouters: try: if storagerouter.ip not in workload: continue node_workload = workload[storagerouter.ip] client = SSHClient(storagerouter) for cluster, ip in node_workload['clusters']: try: GenericController._logger.debug(' Collapsing cluster {0} on {1}'.format(cluster, storagerouter.ip)) client.run(['arakoon', '--collapse-local', node_workload['node_id'], '2', '-config', cluster_config_map[cluster].external_config_path]) GenericController._logger.debug(' Collapsing cluster {0} on {1} completed'.format(cluster, storagerouter.ip)) except: GenericController._logger.exception(' Collapsing cluster {0} on {1} failed'.format(cluster, storagerouter.ip)) except UnableToConnectException: GenericController._logger.error(' Could not collapse any cluster on {0} (not reachable)'.format(storagerouter.name)) GenericController._logger.info('Arakoon collapse finished')
def get_run_file_path(self, name): # type: (str) -> str """ Get the path to the run file for the given service This is tied to the template files as they specify something like `/opt/OpenvStorage/run/<SERVICE_NAME>.version` :param name: Name of the service :type name: str :return: Path to the file :rtype: str """ non_ovs_name = ExtensionsToolbox.remove_prefix(name, self.OVS_SERVICE_PREFIX) return os.path.join(self._run_file_dir, non_ovs_name, '.version')
def unregister_service(self, node_name, service_name): # type: (str, str) -> None """ Un-register the metadata of a service from the configuration management :param node_name: Name of the node on which to un-register the service :type node_name: str :param service_name: Name of the service to clean from the configuration management :type service_name: str :return: None :rtype: NoneType """ self._configuration.delete(key=self.service_config_key.format(node_name, ExtensionsToolbox.remove_prefix(service_name, self.OVS_SERVICE_PREFIX)))
def register_service(self, node_name, service_metadata): # type: (str, dict) -> None """ Register the metadata of the service to the configuration management :param node_name: Name of the node on which the service is running :type node_name: str :param service_metadata: Metadata of the service :type service_metadata: dict :return: None :rtype: NoneType """ service_name = service_metadata['SERVICE_NAME'] self._configuration.set(key=self.service_config_key.format(node_name, ExtensionsToolbox.remove_prefix(service_name, self.OVS_SERVICE_PREFIX)), value=service_metadata)
def add_service(self, name, client, params=None, target_name=None, startup_dependency=None, delay_registration=False, path=None): """ Adds a mocked service """ _ = path if params is None: params = {} key = 'None' if client is None else client.ip name = name if target_name is None else target_name params.update({'SERVICE_NAME': ExtensionsToolbox.remove_prefix(name, 'ovs-'), 'STARTUP_DEPENDENCY': '' if startup_dependency is None else '{0}.service'.format(startup_dependency)}) if self.has_service(name=name, client=client) is False: if key not in SystemdMock.services: SystemdMock.services[key] = {} SystemdMock.services[key].update({name: {'state': 'HALTED', 'pid': None}}) if delay_registration is False: self.register_service(node_name=self._system.get_my_machine_id(client), service_metadata=params) return params