Дмитрий
3 years ago
3 changed files with 102 additions and 128 deletions
@ -1,33 +1,57 @@ |
|||||||
from dataclasses import dataclass |
from dataclasses import dataclass |
||||||
import asyncio |
import asyncio |
||||||
import os |
import os |
||||||
|
import aiofiles |
||||||
|
|
||||||
|
|
||||||
@dataclass |
|
||||||
class ConfigObject: |
class ConfigObject: |
||||||
host: str |
host: str |
||||||
conf_body: str |
conf_body: str |
||||||
path: str |
path: str |
||||||
|
|
||||||
|
def __init__(self, host: str, conf_body: str, path: str): |
||||||
|
self.host = host |
||||||
|
self.conf_body = conf_body.replace( |
||||||
|
"server_name _;", f"server_name {host}.server.com;" |
||||||
|
) |
||||||
|
|
||||||
|
self.path = os.path.abspath(path) |
||||||
|
|
||||||
|
if not os.path.isdir(self.path): |
||||||
|
os.mkdir(self.path) |
||||||
|
|
||||||
|
self.full_path_to_file: str = os.path.join(self.path, f"{host}.conf") |
||||||
|
|
||||||
@property |
@property |
||||||
def existst(self) -> bool: |
def existst(self) -> bool: |
||||||
"""Возвращает True, если файл конфига уже существует.""" |
"""Возвращает True, если файл конфига уже существует.""" |
||||||
return path.isfile(path) |
return os.path.isfile(self.full_path_to_file) |
||||||
|
|
||||||
async def write(self) -> bool: |
async def write(self) -> bool: |
||||||
"""Записывает конфиг файл.""" |
"""Записывает конфиг файл.""" |
||||||
_config_file_name: str = path.join(path, f"{host}.conf") |
|
||||||
|
|
||||||
if not self.existst: |
if not self.existst: |
||||||
pass |
async with aiofiles.open(self.full_path_to_file, mode="w") as file: |
||||||
|
await file.write(self.conf_body) |
||||||
return True |
return True |
||||||
|
|
||||||
|
def __repr__(self): |
||||||
|
return f"Hi, config for {self.host}" |
||||||
|
|
||||||
|
|
||||||
|
class ConfigFactory: |
||||||
|
def __init__(self, path_to_template: str, path_to_configs_dir: str): |
||||||
|
self.templ = self.__read_config_template_file(path_to_template) |
||||||
|
self.path = path_to_configs_dir |
||||||
|
|
||||||
|
def create(self, host: str) -> ConfigObject: |
||||||
|
return ConfigObject(host, self.templ, self.path) |
||||||
|
|
||||||
|
def __read_config_template_file(self, path_to_file: str) -> str: |
||||||
|
"""Прочесть шаблон конфига для сервера из файла.""" |
||||||
|
template: str = "" |
||||||
|
|
||||||
def read_config_template_file(path_to_file: str) -> str: |
_full_path = os.path.abspath(path_to_file) |
||||||
"""Прочесть шаблон конфига для сервера из файла.""" |
with open(_full_path, mode="r", encoding="utf8") as file: |
||||||
template: str = "" |
template = file.read() |
||||||
_full_path = os.path.abspath(path_to_file) |
|
||||||
with open(_full_path, mode="r", encoding="utf8") as file: |
|
||||||
template = file.read() |
|
||||||
|
|
||||||
return template |
return template |
||||||
|
@ -1,140 +1,80 @@ |
|||||||
from dataclasses import dataclass |
import argparse |
||||||
|
import asyncio |
||||||
|
import os |
||||||
from asyncio import Task |
from asyncio import Task |
||||||
|
from dataclasses import dataclass |
||||||
from typing import List |
from typing import List |
||||||
from aiohttp.client_exceptions import ClientConnectorError |
|
||||||
|
|
||||||
import asyncio |
|
||||||
import os |
|
||||||
import aiohttp |
import aiohttp |
||||||
import aiofiles |
from aiohttp.client_exceptions import ClientConnectorError |
||||||
|
from loguru import logger |
||||||
|
|
||||||
from config_object import ConfigObject, read_config_template_file |
|
||||||
from app_config import AppConfig |
from app_config import AppConfig |
||||||
from request_builder import RequestBulder |
from config_object import ConfigFactory, ConfigObject |
||||||
|
from request_builder import Json, RequestBulder |
||||||
|
|
||||||
async def write_config_files(hosts: list, cfg: AppConfig, template: str) -> None: |
|
||||||
"""Записываем конфиги для списка hosts.""" |
|
||||||
|
|
||||||
full_path_to_config_dir: str = os.path.abspath(cfg.path_for_config) |
|
||||||
|
|
||||||
if not os.path.isdir(full_path_to_config_dir): |
|
||||||
os.mkdir(full_path_to_config_dir) |
|
||||||
|
|
||||||
for host in hosts: |
|
||||||
config_filename: str = f"{host}.conf" |
|
||||||
config_file = os.path.join(full_path_to_config_dir, config_filename) |
|
||||||
|
|
||||||
if not os.path.isfile(config_file): |
|
||||||
config_body: str = template.replace( |
|
||||||
"server_name _;", f"server_name {host}.server.com;" |
|
||||||
) |
|
||||||
async with aiofiles.open(config_file, mode="w") as file: |
|
||||||
await file.write(config_body) |
|
||||||
|
|
||||||
|
|
||||||
def _get_template(templ_file: str) -> str: |
|
||||||
"""Возвращает шаблон конфига для хоста из файла `templ_file`.""" |
|
||||||
|
|
||||||
template: str = "" |
|
||||||
with open(templ_file, mode="r", encoding="utf8") as file: |
|
||||||
template = file.read() |
|
||||||
|
|
||||||
return template |
|
||||||
|
|
||||||
|
|
||||||
async def get_records_count(session, server) -> int: |
async def get_records_count(rb: RequestBulder) -> int: |
||||||
"""Возвращает количество записей в базе.""" |
resp: Json | bool = await rb.send_request("count", json_body={}) |
||||||
async with session.get(f"{server}/count") as resp: |
records_count = 0 |
||||||
resp = await resp.json() |
if resp: |
||||||
count: int = int(resp["result"]) |
records_count = resp.get("result") |
||||||
return count |
|
||||||
|
|
||||||
|
|
||||||
async def get_tasks( |
|
||||||
url: str, portion: int, count: int, session: aiohttp.ClientSession, body: dict |
|
||||||
) -> List[Task]: |
|
||||||
"""Вернет список задач с запросами к API. |
|
||||||
|
|
||||||
Функция не ограничивает кол-во запросов, это нужно сделать до |
|
||||||
вызова, чтобы передать корректный `session`. |
|
||||||
|
|
||||||
Parameters |
|
||||||
---------- |
|
||||||
url : str |
|
||||||
Куда слать запрос. Ожидается "server/get" |
|
||||||
portion : int |
|
||||||
Сколько записей запрашивать за раз |
|
||||||
count : int |
|
||||||
Общее количество записей |
|
||||||
session : aiohttp.ClientSession |
|
||||||
Объект сессии, создается в уровне выше, с одним объектом |
|
||||||
меньше накладных расходов на каждый запрос. |
|
||||||
body : json |
|
||||||
Json для запроса. |
|
||||||
""" |
|
||||||
tasks: List[Task] = [] |
|
||||||
|
|
||||||
for offset in range(0, count, portion): |
|
||||||
tasks.append(asyncio.create_task(session.get(url, json=body))) |
|
||||||
body["offset"] = offset |
|
||||||
|
|
||||||
return tasks |
|
||||||
|
|
||||||
|
|
||||||
async def send_async_request(cfg: AppConfig, json_body: dict) -> None: |
|
||||||
"""Начать серию запросов.""" |
|
||||||
template: str = _get_template(cfg.template) |
|
||||||
|
|
||||||
# ограничим одновременное число запросов |
|
||||||
conn = aiohttp.TCPConnector(limit=cfg.requests_count) |
|
||||||
|
|
||||||
url = cfg.central_host_url |
|
||||||
portion = cfg.request_portion |
|
||||||
|
|
||||||
try: |
return records_count |
||||||
async with aiohttp.ClientSession(connector=conn) as session: |
|
||||||
# получаем количесвто записей |
|
||||||
count = await get_records_count(session, url) |
|
||||||
|
|
||||||
tasks = await get_tasks(f"{url}/get", portion, count, session, json_body) |
|
||||||
|
|
||||||
responses = await asyncio.gather(*tasks) |
async def custom_wrapper( |
||||||
|
config_factory: ConfigFactory, |
||||||
|
rb: RequestBulder, |
||||||
|
usr: str, |
||||||
|
json: Json, |
||||||
|
) -> None: |
||||||
|
resp: Json | bool = await rb.send_request("get", json) |
||||||
|
|
||||||
# Пройдемся по ответам на запросы, запишем файлы конфига для |
# Если мы получили валидный ответ, то разбираем пачку хостов из |
||||||
# каждого respone. Каждый response содержит portion или меньше хостов |
# resp['result'] с созданием для каждой строки кофнига через |
||||||
for response in responses: |
# фабрику |
||||||
resp = await response.json() |
if resp: |
||||||
hosts = [i["hostname"] for i in resp.get("result")] |
conf_list = [config_factory.create(host["hostname"]) for host in resp["result"]] |
||||||
await write_config_files(hosts, cfg, template) |
await asyncio.gather(*[c.write() for c in conf_list]) |
||||||
except ClientConnectorError: |
else: |
||||||
print(f"Невозможно подключиться к серверу {url}") |
logger.error(f"Сервер вернул ошибку") |
||||||
|
|
||||||
|
|
||||||
async def main() -> None: |
async def main(config_path: str) -> None: |
||||||
"""Точка входа.""" |
"""Точка входа.""" |
||||||
cfg: AppConfig = AppConfig("service.conf") |
cfg: AppConfig = AppConfig(config_path) |
||||||
print(cfg.configs_path) |
|
||||||
|
|
||||||
rb = RequestBulder(cfg) |
rb = RequestBulder(cfg) |
||||||
|
|
||||||
resp = await rb.send_request("count", json_body={}) |
while True: |
||||||
|
records_count = await get_records_count(rb) |
||||||
if resp: |
|
||||||
records_count = resp.get("result") |
|
||||||
|
|
||||||
print(records_count) |
|
||||||
|
|
||||||
body_json = {"columns": "['hostname']", "limit": cfg.request_portion, "offset": 0} |
|
||||||
|
|
||||||
template = read_config_template_file(cfg.template) |
json_boby_list = [] |
||||||
|
for offset in range(0, records_count, cfg.request_portion): |
||||||
|
body_json = { |
||||||
|
"columns": "['hostname']", |
||||||
|
"limit": cfg.request_portion, |
||||||
|
"offset": offset, |
||||||
|
} |
||||||
|
json_boby_list.append(body_json) |
||||||
|
|
||||||
print(template) |
conf_factory = ConfigFactory(cfg.template, cfg.path_for_config) |
||||||
# while True: |
await asyncio.gather( |
||||||
|
*[custom_wrapper(conf_factory, rb, "get", jb) for jb in json_boby_list] |
||||||
|
) |
||||||
|
|
||||||
# await send_async_request(cfg, json_body=body) |
await rb.wait() |
||||||
# await asyncio.sleep(cfg.frequency_sec) |
# закрываем сессию |
||||||
|
await rb.close() |
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": |
if __name__ == "__main__": |
||||||
asyncio.run(main()) |
parser = argparse.ArgumentParser(description="Service for nginx config creation.") |
||||||
|
parser.add_argument( |
||||||
|
"--config_path", required=True, type=str, help="path for conifg file" |
||||||
|
) |
||||||
|
args = parser.parse_args() |
||||||
|
config_path = os.path.abspath(args.config_path) |
||||||
|
|
||||||
|
asyncio.run(main(config_path)) |
||||||
|
Loading…
Reference in new issue