def handle_api_operate(self, http_context, manager_id=None, operation=None, service_id=None): if operation not in ['start', 'stop', 'restart']: return try: getattr(self.managers[manager_id], operation)(service_id) except ServiceOperationError as e: raise EndpointError(e)
def handle_api_fs_write(self, http_context, path=None): try: content = http_context.body if http_context.query: encoding = http_context.query.get('encoding', None) if encoding: content = content.decode('utf-8').encode(encoding) with open(path, 'w') as f: f.write(content) except OSError as e: raise EndpointError(e)
def handle_api_fs_list(self, http_context, path=None): """ Return a list of objects (files, directories, ...) in a specific directory, and their informations. :param http_context: HttpContext :type http_context: HttpContext :param path: Directory path :type path: string :return: All items with informations :rtype: dict """ if not os.path.exists(path): raise EndpointReturn(404) try: items = [] for name in os.listdir(path): item_path = os.path.join(path, name) data = { 'name': name, 'path': item_path, 'isDir': os.path.isdir(item_path), 'isFile': os.path.isfile(item_path), 'isLink': os.path.islink(item_path), } try: stat = os.stat(item_path) data.update({ 'mode': stat.st_mode, 'mtime': stat.st_mtime, 'uid': stat.st_uid, 'gid': stat.st_gid, 'size': stat.st_size, }) except OSError as e: data['accessError'] = str(e) if e.errno == errno.ENOENT and os.path.islink(item_path): data['brokenLink'] = True items.append(data) return { 'parent': os.path.dirname(os.path.normpath(path)) if path != '/' else None, 'items': items, } except OSError as e: raise EndpointError(e)
def handle_api_remove_image(self, http_context, image_id=None): """ Delete one image (given as hash) on the docker host. :param http_context: HttpContext :type http_context: HttpContext """ command = self.docker + ['rmi', image_id] try: subprocess.check_output(command, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise EndpointError(e.output.decode().strip())
def handle_api_which_docker(self, http_context): """ Test if docker is installed and retrieve docker version. :param http_context: HttpContext :type http_context: HttpContext """ try: self.docker = subprocess.check_output(['which', 'docker']).decode().strip() except subprocess.CalledProcessError as e: raise EndpointError(_('Docker is not installed on this host'))
def handle_api_fs_create_directory(self, http_context, path=None): """ Create empty directory on specified path. :param http_context: HttpContext :type http_context: HttpContext :param path: Path of directory :type path: string """ try: os.makedirs(path) except OSError as e: raise EndpointError(e)
def handle_api_fs_create_file(self, http_context, path=None): """ Create empty file on specified path. :param http_context: HttpContext :type http_context: HttpContext :param path: Path of file :type path: string """ try: os.mknod(path, int('644', 8)) except OSError as e: raise EndpointError(e)
def get_json_info(plugin): """ Request plugin informations from pypi and append it to the plugins list. :param plugin: List of plugins names :type plugin: list """ try: url = f'https://pypi.python.org/pypi/{plugin}/json' data = requests.get(url).json() plugin_list.append(filter_info(data)) except Exception as e: raise EndpointError(e)
def handle_api_remove_image(self, http_context): """ Delete one image (given as hash) on the docker host. Method POST. :param http_context: HttpContext :type http_context: HttpContext """ if http_context.method == 'POST': image = http_context.json_body()['image'] command = self.docker + ['rmi', image] try: subprocess.check_output(command, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise EndpointError(e.output.decode().strip())
def handle_api_umount(self, http_context): """ Umount some device. :param http_context: HttpContext :type http_context: HttpContext :return: Success or not :rtype: bool or throw error """ mountpoint = http_context.json_body()['mountpoint'] try: subprocess.check_output(['umount', mountpoint]) return True except Exception as e: raise EndpointError(e)
def handle_api_hosts(self, http_context): """ Load (through get) or save ( through post) the hosts file, and always make a backup before saving. Method GET. Method POST. :param http_context: HttpContext :type http_context: HttpContext :return: File content in load mode, success or error in save mode :rtype: dict in load mode, bool or throw error in save mode """ if http_context.method == 'GET': self.hosts_config = HostsConfig(path='/etc/hosts') self.hosts_config.load() return self.hosts_config.tree.to_dict() if http_context.method == 'POST': config = http_context.json_body()['config'] new_hosts = HostsConfig(content='') new_hosts.load() for host in config: new_host = HostData() for prop, value in host.items(): if prop == 'aliases': for alias in value: if alias['name'] != '': new_alias = AliasData() new_alias.name = alias['name'] new_host.aliases.append(new_alias) else: setattr(new_host, prop, value) new_hosts.tree.hosts.append(new_host) data = new_hosts.save()[None] # Always make a backup os.rename('/etc/hosts', '/etc/hosts.bak') try: with open('/etc/hosts', 'w') as f: f.write(data) return True except Exception as e: raise EndpointError(e)
def handle_api_pypi_uninstall(self, http_context, name=None): """ Uninstall ajenti packages with pip3. :param http_context: HttpContext :type http_context: HttpContext :param name: Package name :type name: string """ try: subprocess.check_output([ 'python3', '-m', 'pip', 'uninstall', '-y', 'ajenti.plugin.%s' % name ]) except subprocess.CalledProcessError as e: raise EndpointError(e.output)
def handle_api_fs_chmod(self, http_context, path=None): """ Change mode for a specific file. :param http_context: HttpContext :type http_context: HttpContext :param path: Path of file :type path: string """ if not os.path.exists(path): raise EndpointReturn(404) data = json.loads(http_context.body.decode()) try: os.chmod(path, data['mode']) except OSError as e: raise EndpointError(e)
def handle_api_container_stop(self, http_context): """ Some controls for container : start, stop and remove. The hash of the container is sent per POST. Method POST. :param http_context: HttpContext :type http_context: HttpContext """ if http_context.method == 'POST': container = http_context.json_body()['container'] control = http_context.json_body()['control'] command = self.docker + [control, container] try: subprocess.check_output(command, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise EndpointError(e.output.decode().strip())
def handle_api_users_apply(self, http_context): path = '/tmp/sophomorix.log' open(path, 'w').close() script = '' if http_context.json_body()['doAdd']: script += 'sophomorix-add >> %s;' % path if http_context.json_body()['doMove']: script += 'sophomorix-move >> %s;' % path if http_context.json_body()['doKill']: script += 'sophomorix-kill >> %s;' % path try: subprocess.check_call(script, shell=True, env={'LC_ALL': 'C'}) except Exception as e: raise EndpointError(None, message=str(e))
def handle_api_calculate(self, http_context, operation=None, a=None, b=None): start_time = time.time() try: if operation == 'add': result = int(a) + int(b) elif operation == 'divide': result = int(a) / int(b) else: raise EndpointReturn(404) except ZeroDivisionError: raise EndpointError('Division by zero') return {'value': result, 'time': time.time() - start_time}
def handle_api_core_check_upgrade(self, http_context): """ Check last published version of ajenti. :param http_context: HttpContext :type http_context: HttpContext :return: Last version number :rtype: string """ url = 'https://pypi.python.org/pypi/ajenti-panel/json' version = None try: data = requests.get(url).json() version = data['info']['version'] except Exception as e: raise EndpointError(e) return version
def handle_api_fs_write(self, http_context, path=None): """ Write content (method post) to a specific file given with path. :param http_context: HttpContext :type http_context: HttpContext :param path: Path of the file :type path: string """ try: content = http_context.body if http_context.query: encoding = http_context.query.get('encoding', None) if encoding: content = content.decode('utf-8') with open(path, 'w') as f: f.write(content) except OSError as e: raise EndpointError(e)
def handle_api_pypi_install(self, http_context, name=None, version=None): """ Install ajenti packages with pip3. :param http_context: HttpContext :type http_context: HttpContext :param name: Package name :type name: string :param version: Version number :type version: string """ # TODO replaced with a task try: subprocess.call([ 'python3', '-m', 'pip', 'install', 'ajenti.plugin.%s==%s' % (name, version) ]) except subprocess.CalledProcessError as e: raise EndpointError(e.output)
def handle_api_container_stop(self, http_context): """ Some controls for container : start, stop and remove. :param http_context: HttpContext :type http_context: HttpContext """ container_id = http_context.json_body()['container_id'] control = http_context.json_body()['control'] if control in ['start', 'stop', 'rm']: command = self.docker + [control, container_id] else: return http_context.respond_not_found() try: subprocess.check_output(command, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: raise EndpointError(e.output.decode().strip())
def handle_api_fs_list(self, http_context, path=None): if not os.path.exists(path): raise EndpointReturn(404) try: items = [] for name in os.listdir(path): item_path = os.path.join(path, name) data = { 'name': name, 'path': item_path, 'isDir': os.path.isdir(item_path), 'isFile': os.path.isfile(item_path), 'isLink': os.path.islink(item_path), } try: stat = os.stat(item_path) data.update({ 'mode': stat.st_mode, 'mtime': stat.st_mtime, 'uid': stat.st_uid, 'gid': stat.st_gid, 'size': stat.st_size, }) except OSError as e: data['accessError'] = str(e) if e.errno == errno.ENOENT and os.path.islink(item_path): data['brokenLink'] = True items.append(data) return { 'parent': os.path.dirname(os.path.normpath(path)) if path != '/' else None, 'items': items, } except OSError as e: raise EndpointError(e)
def handle_api_users_check(self, http_context): path = '/tmp/sophomorix-check.log' open(path, 'w').close() try: subprocess.check_call('sophomorix-check > %s' % path, shell=True, env={'LC_ALL': 'C'}) except Exception as e: raise EndpointError(str(e)) results = { 'add': [], 'move': [], 'kill': [], 'errors': [], 'report': open('/var/lib/sophomorix/check-result/report.admin').read().decode('utf-8', errors='ignore'), } lines = open('/tmp/sophomorix-check.log').read().decode('utf-8', errors='ignore').splitlines() while lines: l = lines.pop(0) if 'Fehlerhafter Datensatz' in l: s = '' while lines[0][0] != '#': s += lines[0].strip() + '\n' lines.pop(0) results['errors'].append(s) if 'Looking for tolerated users to be moved/deactivated' in l or 'Looking for users to be tolerated' in l: while lines[0][0] != '#': s = lines.pop(0).strip() if '--->' in s: results['move'].append(s) if 'Looking for users to be added' in l: while lines[0][0] != '#': results['add'].append(lines.pop(0).strip()) if 'killable users to be killed' in l: while lines[0][0] != '#': s = lines.pop(0).strip() if '--->' in s: results['kill'].append(s) return results
def handle_api_getpypi_list(self, http_context): def filter_info(plugin): name = plugin['info']['name'].split('.')[-1] return { "url": plugin['info']['project_urls']['Homepage'], "version": plugin['info']['version'], "description": plugin['info']['description'], "name": name, "title": plugin['info']['summary'], "author_email": plugin['info']['author_email'], "last_month_downloads": plugin['info']['downloads']['last_month'], "author": plugin['info']['author'], "pypi_name": plugin['info']['name'], "type": "official" if name in official else "community", } def get_json_info(plugin): try: url = 'https://pypi.python.org/pypi/%s/json' % plugin data = requests.get(url).json() plugin_list.append(filter_info(data)) except Exception as e: raise EndpointError(e) if os.path.exists('/root/.cache/pip'): shutil.rmtree('/root/.cache/pip') try: plugin_list = [] page = requests.get('https://pypi.org/simple') official = requests.get( 'https://raw.githubusercontent.com/ajenti/ajenti/master/official_plugins.json' ).json()['plugins'] pypi_plugin_list = fromstring(page.content).xpath( "//a[starts-with(text(),'ajenti.plugin')]/text()") with futures.ThreadPoolExecutor(20) as executor: res = executor.map(get_json_info, pypi_plugin_list) return plugin_list except Exception as e: raise EndpointError(e)
def handle_api_fstab(self, http_context): """ Load (through get) and write (through post) the fstab file. Make a backup when save a new fstab file. Method GET. Method POST. :param http_context: HttpContext :type http_context: HttpContext :return: Fstab as dict in load mode, success or not in save mode :rtype: dict in load mode, bool or throw error in save mode """ if http_context.method == 'GET': self.fstab_config = FSTabConfig(path='/etc/fstab') self.fstab_config.load() return self.fstab_config.tree.to_dict() if http_context.method == 'POST': config = http_context.json_body()['config'] new_fstab = FSTabConfig(content='') new_fstab.load() for filesystem in config: device = FilesystemData() for prop, value in filesystem.items(): setattr(device, prop, value) new_fstab.tree.filesystems.append(device) data = new_fstab.save()[None] # Always make a backup os.rename('/etc/fstab', '/etc/fstab.bak') try: with open('/etc/fstab', 'w') as f: f.write(data) return True except Exception as e: raise EndpointError(e)
def handle_api_save_crontab(self, http_context): if http_context.method == 'POST': ## Create empty config user = self.context.identity crontab = CronManager.get(self.context).load_tab(None) new_crontab = http_context.json_body()['crontab'] for _type, values_list in new_crontab.items(): for values in values_list: if _type == 'normal_tasks': crontab.tree.normal_tasks.append( self._create_normal_task(values)) elif _type == 'special_tasks': crontab.tree.special_tasks.append( self._create_special_task(values)) elif _type == 'env_settings': crontab.tree.env_settings.append( self._create_env_settings(values)) try: CronManager.get(self.context).save_tab(user, crontab) return True except Exception as e: raise EndpointError(e)
def handle_api_hosts(self, http_context): if http_context.method == 'GET': self.hosts_config = HostsConfig(path='/etc/hosts') self.hosts_config.load() return self.hosts_config.tree.to_dict() if http_context.method == 'POST': config = http_context.json_body()['config'] new_hosts = HostsConfig(content='') new_hosts.load() for host in config: new_host = HostData() for property, value in host.items(): if property == 'aliases': for alias in value: if alias['name'] != '': new_alias = AliasData() new_alias.name = alias['name'] new_host.aliases.append(new_alias) else: setattr(new_host, property, value) new_hosts.tree.hosts.append(new_host) data = new_hosts.save()[None] # Always make a backup os.rename('/etc/hosts', '/etc/hosts.bak') try: with open('/etc/hosts', 'w') as f: f.write(data) return True except Exception as e: raise EndpointError(e)
def handle_api_fs_create_directory(self, http_context, path=None): try: os.makedirs(path) except OSError as e: raise EndpointError(e)
def handle_api_fs_create_file(self, http_context, path=None): try: os.mknod(path, int('644', 8)) except OSError as e: raise EndpointError(e)
def handle_api_fs_write(self, http_context, path=None): try: with open(path, 'w') as f: f.write(http_context.body) except OSError as e: raise EndpointError(e)
def handle_api_apply(self, http_context): try: subprocess.check_call( 'sophomorix-quota > /tmp/apply-sophomorix.log', shell=True) except Exception as e: raise EndpointError(None, message=str(e))