8cb60f0833
1. move skyline_apiserver out of libs 2. remove libs folder 3. remove old skyline folder 4. adjust zull, devstack and dockerfile Change-Id: I27a4babd3df077d1dfc7555f67a6ea618d4b2966
168 lines
5.5 KiB
Python
168 lines
5.5 KiB
Python
# Copyright 2021 99cloud
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from __future__ import annotations
|
|
|
|
import warnings
|
|
from dataclasses import InitVar, dataclass, field
|
|
from pathlib import Path, PurePath
|
|
from typing import Any, Dict, Iterator, NamedTuple, Sequence, Tuple, Type
|
|
|
|
import yaml
|
|
from immutables import Map, MapItems, MapKeys, MapValues
|
|
from pydantic import BaseModel, create_model
|
|
|
|
|
|
class ConfigPath(NamedTuple):
|
|
config_dir_path: str
|
|
config_file_path: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Opt:
|
|
name: str
|
|
description: str
|
|
schema: Any
|
|
default: Any = None
|
|
deprecated: bool = False
|
|
value: Any = field(init=False, default=None)
|
|
_schema_model: Type[BaseModel] = field(init=False, repr=False)
|
|
|
|
def __post_init__(self) -> None:
|
|
object.__setattr__(
|
|
self,
|
|
"_schema_model",
|
|
create_model(f"Opt(name='{self.name}')", value=(self.schema, ...)),
|
|
)
|
|
|
|
def load(self, value: Any) -> None:
|
|
value = self.default if value is None else value
|
|
self._schema_model(value=value)
|
|
object.__setattr__(self, "value", value)
|
|
if self.deprecated:
|
|
warnings.warn(
|
|
f"The config opt {self.name} is deprecated, will be deleted in the"
|
|
" future version",
|
|
DeprecationWarning,
|
|
)
|
|
|
|
|
|
@dataclass(repr=False, frozen=True)
|
|
class Group:
|
|
name: str
|
|
init_opts: InitVar[Sequence[Opt]] = tuple()
|
|
_opts: Map[str, Opt] = field(init=False, repr=False)
|
|
|
|
def __post_init__(self, init_opts: Sequence[Opt]) -> None:
|
|
object.__setattr__(self, "_opts", Map({opt.name: opt for opt in init_opts}))
|
|
|
|
def __getattr__(self, name: str) -> Any:
|
|
if name in self._opts:
|
|
return self._opts[name].value
|
|
raise AttributeError(name)
|
|
|
|
def __contains__(self, key: Any) -> bool:
|
|
return self._opts.__contains__(key)
|
|
|
|
def __iter__(self) -> Iterator[Any]:
|
|
return self._opts.__iter__()
|
|
|
|
def __len__(self) -> int:
|
|
return self._opts.__len__()
|
|
|
|
def __repr__(self) -> str:
|
|
items = ", ".join((f"{opt}=Opt(name='{opt}')" for opt in self._opts))
|
|
return f"Group({items})"
|
|
|
|
def keys(self) -> MapKeys[str]:
|
|
return self._opts.keys()
|
|
|
|
def values(self) -> MapValues[Opt]:
|
|
return self._opts.values()
|
|
|
|
def items(self) -> MapItems[str, Opt]:
|
|
return self._opts.items()
|
|
|
|
|
|
@dataclass(repr=False, frozen=True)
|
|
class Configuration:
|
|
init_groups: InitVar[Sequence[Group]] = tuple()
|
|
config: Dict[str, Any] = field(init=False, default_factory=dict, repr=False)
|
|
_groups: Map[str, Group] = field(init=False, repr=False)
|
|
|
|
def __post_init__(self, init_groups: Sequence[Group]) -> None:
|
|
object.__setattr__(self, "_groups", Map({group.name: group for group in init_groups}))
|
|
|
|
@staticmethod
|
|
def get_config_path(project: str, env: Dict[str, str]) -> Tuple[str, str]:
|
|
config_dir_path = env.get("OS_CONFIG_DIR", PurePath("/etc", project).as_posix())
|
|
config_file_path = PurePath(config_dir_path).joinpath(f"{project}.yaml").as_posix()
|
|
return ConfigPath(config_dir_path.strip(), config_file_path.strip())
|
|
|
|
def setup(self, project: str, env: Dict[str, str]) -> None:
|
|
config_dir_path, config_file_path = self.get_config_path(project, env)
|
|
if not Path(config_file_path).exists():
|
|
raise ValueError(f"Not found config file: {config_file_path}")
|
|
|
|
with open(config_file_path) as f:
|
|
try:
|
|
object.__setattr__(self, "config", yaml.safe_load(f))
|
|
except Exception:
|
|
raise ValueError("Load config file error")
|
|
|
|
for group in self._groups.values():
|
|
for opt in group._opts.values():
|
|
value = self.config.get(group.name, {}).get(opt.name)
|
|
opt.load(value)
|
|
|
|
def cleanup(self) -> None:
|
|
for group in self._groups.values():
|
|
for opt in group._opts.values():
|
|
object.__setattr__(opt, "value", None)
|
|
object.__setattr__(self, "_groups", Map())
|
|
object.__setattr__(self, "config", {})
|
|
|
|
def __call__(self, init_groups: Sequence[Group]) -> Any:
|
|
object.__setattr__(self, "_groups", Map({group.name: group for group in init_groups}))
|
|
|
|
def __getattr__(self, name: str) -> Group:
|
|
if name in self._groups:
|
|
return self._groups[name]
|
|
raise AttributeError(name)
|
|
|
|
def __contains__(self, key: Any) -> bool:
|
|
return self._groups.__contains__(key)
|
|
|
|
def __iter__(self) -> Iterator[Any]:
|
|
return self._groups.__iter__()
|
|
|
|
def __len__(self) -> int:
|
|
return self._groups.__len__()
|
|
|
|
def __repr__(self) -> str:
|
|
items = ", ".join((f"{group}=Group(name='{group}')" for group in self._groups))
|
|
return f"Configuration({items})"
|
|
|
|
def keys(self) -> MapKeys[str]:
|
|
return self._groups.keys()
|
|
|
|
def values(self) -> MapValues[Group]:
|
|
return self._groups.values()
|
|
|
|
def items(self) -> MapItems[str, Group]:
|
|
return self._groups.items()
|
|
|
|
|
|
__all__ = ("Opt", "Group", "Configuration")
|