Browse Source

Рабочая версия

master
Дмитрий 3 years ago
parent
commit
484b2af669
  1. 48
      config_object.py
  2. 10
      server.py
  3. 172
      solution.py

48
config_object.py

@ -1,33 +1,57 @@
from dataclasses import dataclass
import asyncio
import os
import aiofiles
@dataclass
class ConfigObject:
host: str
conf_body: 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
def existst(self) -> bool:
"""Возвращает True, если файл конфига уже существует."""
return path.isfile(path)
return os.path.isfile(self.full_path_to_file)
async def write(self) -> bool:
"""Записывает конфиг файл."""
_config_file_name: str = path.join(path, f"{host}.conf")
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
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:
"""Прочесть шаблон конфига для сервера из файла."""
template: str = ""
_full_path = os.path.abspath(path_to_file)
with open(_full_path, mode="r", encoding="utf8") as file:
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

10
server.py

@ -15,6 +15,8 @@ curl --request GET \
from flask import Flask, jsonify, request
import json
import secrets
from random import randint
import time
app = Flask(__name__)
@ -43,7 +45,15 @@ def index():
for host in range(0, limit):
hosts.append({"hostname": f"content-creator-{secrets.token_hex(4)}"})
time.sleep(randint(1, 6))
return jsonify({"result": hosts, "done": True})
@app.route("/api/portal/count", methods=["GET"])
def count():
time.sleep(randint(1, 2))
return jsonify({"result": randint(20, 300), "done": True})
app.run()

172
solution.py

@ -1,140 +1,80 @@
from dataclasses import dataclass
import argparse
import asyncio
import os
from asyncio import Task
from dataclasses import dataclass
from typing import List
from aiohttp.client_exceptions import ClientConnectorError
import asyncio
import os
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 request_builder import 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
from config_object import ConfigFactory, ConfigObject
from request_builder import Json, RequestBulder
async def get_records_count(session, server) -> int:
"""Возвращает количество записей в базе."""
async with session.get(f"{server}/count") as resp:
resp = await resp.json()
count: int = int(resp["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
async def get_records_count(rb: RequestBulder) -> int:
resp: Json | bool = await rb.send_request("count", json_body={})
records_count = 0
if resp:
records_count = resp.get("result")
try:
async with aiohttp.ClientSession(connector=conn) as session:
# получаем количесвто записей
count = await get_records_count(session, url)
return records_count
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 или меньше хостов
for response in responses:
resp = await response.json()
hosts = [i["hostname"] for i in resp.get("result")]
await write_config_files(hosts, cfg, template)
except ClientConnectorError:
print(f"Невозможно подключиться к серверу {url}")
# Если мы получили валидный ответ, то разбираем пачку хостов из
# resp['result'] с созданием для каждой строки кофнига через
# фабрику
if resp:
conf_list = [config_factory.create(host["hostname"]) for host in resp["result"]]
await asyncio.gather(*[c.write() for c in conf_list])
else:
logger.error(f"Сервер вернул ошибку")
async def main() -> None:
async def main(config_path: str) -> None:
"""Точка входа."""
cfg: AppConfig = AppConfig("service.conf")
print(cfg.configs_path)
cfg: AppConfig = AppConfig(config_path)
rb = RequestBulder(cfg)
resp = await rb.send_request("count", json_body={})
if resp:
records_count = resp.get("result")
print(records_count)
body_json = {"columns": "['hostname']", "limit": cfg.request_portion, "offset": 0}
while True:
records_count = await get_records_count(rb)
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)
# while True:
conf_factory = ConfigFactory(cfg.template, cfg.path_for_config)
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 asyncio.sleep(cfg.frequency_sec)
await rb.wait()
# закрываем сессию
await rb.close()
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…
Cancel
Save