Skip to content

Commit

Permalink
tests: added cli test plus additional fixes to BPs
Browse files Browse the repository at this point in the history
coveragerc added omit for BF
refactored setup and teardown for reduced boilerplate
added cli actions for core services #450

Change-Id: I0ba862227c6277771b0175be0d8ce72e5896c9ce
  • Loading branch information
grafuls committed Oct 17, 2023
1 parent 1a05a54 commit 1f58eb5
Show file tree
Hide file tree
Showing 69 changed files with 3,080 additions and 1,796 deletions.
6 changes: 5 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
source = quads/cli/cli.py

[run]
omit = tests/*
omit =
tests/*
quads/tools/external/badfish.py

[report]
# Regexes for lines to exclude from consideration
Expand All @@ -24,5 +26,7 @@ exclude_lines =

# Don't complain about abstract methods, they aren't run:
@(abc\.)?abstractmethod
except (APIServerException, APIBadRequest) as ex:
raise CliException(str(ex))

ignore_errors = True
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,5 @@ data_db/
wiki_db/
wordpress_data/
codecov
tests/cli/artifacts/*
tests/tools/artifacts/*
768 changes: 302 additions & 466 deletions quads/cli/cli.py

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions quads/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,69 @@
default=None,
help="Filter search by host metadata",
)
action_group.add_argument(
"--regen-instack",
dest="action",
action="store_const",
const="regen_instack",
help="Regenerate instack JSON",
)
action_group.add_argument(
"--regen-heatmap",
dest="action",
action="store_const",
const="regen_heatmap",
help="Regenerate web table heatmap",
)
action_group.add_argument(
"--regen-wiki",
dest="action",
action="store_const",
const="regen_wiki",
help="Regenerate wiki",
)
action_group.add_argument(
"--foreman-rbac",
dest="action",
action="store_const",
const="foreman_rbac",
help="Regenerate foreman RBAC",
)
action_group.add_argument(
"--notify",
dest="action",
action="store_const",
const="notify",
help="Send notifications for cloud assignments",
)
action_group.add_argument(
"--validate-env",
dest="action",
action="store_const",
const="validate_env",
help="Validate Quads assignments",
)
parser.add_argument(
"--skip-system",
dest="skip_system",
action="store_true",
default=False,
help="Skip system tests, when validating Quads assignments",
)
parser.add_argument(
"--skip-network",
dest="skip_network ",
action="store_true",
default=False,
help="Skip network tests, when validating Quads assignments",
)
parser.add_argument(
"--skip-hosts",
dest="skip_hosts",
action='append',
nargs='*',
help="Skip specific hosts, when validating Quads assignments",
)


if __name__ == "__main__":
Expand Down
66 changes: 35 additions & 31 deletions quads/quads_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
from quads.server.models import Host, Cloud, Schedule, Interface, Vlan, Assignment


class MessengerDTO:
def __init__(self, response):
self.__dict__ = response.json()


class APIServerException(Exception):
pass

Expand All @@ -42,15 +37,11 @@ def __init__(self, config: Config):
self.config = config
self.base_url = config.API_URL
self.session = requests.Session()
self.auth = HTTPBasicAuth(
self.config.get("quads_api_username"), self.config.get("quads_api_password")
)
self.auth = HTTPBasicAuth(self.config.get("quads_api_username"), self.config.get("quads_api_password"))

# Base functions
def get(self, endpoint: str) -> Response:
_response = self.session.get(
os.path.join(self.base_url, endpoint), verify=False, auth=self.auth
)
_response = self.session.get(os.path.join(self.base_url, endpoint), verify=False, auth=self.auth)
if _response.status_code == 500:
raise APIServerException("Check the flask server logs")
if _response.status_code == 400:
Expand Down Expand Up @@ -90,9 +81,7 @@ def patch(self, endpoint, data) -> Response:
return _response

def delete(self, endpoint) -> Response:
_response = self.session.delete(
os.path.join(self.base_url, endpoint), verify=False, auth=self.auth
)
_response = self.session.delete(os.path.join(self.base_url, endpoint), verify=False, auth=self.auth)
if _response.status_code == 500:
raise APIServerException("Check the flask server logs")
if _response.status_code == 400:
Expand Down Expand Up @@ -133,8 +122,8 @@ def filter_assignments(self, data) -> List[Assignment]:
response = self.get(f"assignments?{url_params}")
assignments = []
for ass in response.json():
host_obj = Assignment().from_dict(data=ass)
assignments.append(host_obj)
ass_obj = Assignment().from_dict(data=ass)
assignments.append(ass_obj)
return assignments

def get_host(self, hostname) -> Optional[Host]:
Expand Down Expand Up @@ -169,7 +158,7 @@ def get_clouds(self) -> List[Cloud]:
clouds = []
for cloud in response.json():
clouds.append(Cloud(**cloud))
return clouds
return [cloud for cloud in sorted(clouds, key=lambda x: x.name)]

def filter_clouds(self, data) -> List[Cloud]:
response = self.get("clouds", **data)
Expand Down Expand Up @@ -246,12 +235,14 @@ def get_available(self) -> List[Host]:
return hosts

# Available
def get_moves(self) -> List:
response = self.get("moves")
hosts = []
for host in response.json():
hosts.append(Host(**host))
return hosts
def get_moves(self, date=None) -> List:
url = "moves"
if date:
url_params = url_parse.urlencode({"date": date})
url = f"moves?{url_params}"
response = self.get(url)
data = response.json()
return data

def filter_available(self, data) -> List[Host]:
response = self.get(f"available?{urlencode(data)}")
Expand All @@ -268,8 +259,15 @@ def insert_assignment(self, data) -> Response:
def update_assignment(self, assignment_id, data) -> Response:
return self.patch(os.path.join("assignments", str(assignment_id)), data)

def get_active_cloud_assignment(self, cloud_name) -> Response:
return self.get(os.path.join("assignments/active", cloud_name))
def get_active_cloud_assignment(self, cloud_name) -> List[Assignment]:
response = self.get(os.path.join("assignments/active", cloud_name))
data = response.json()
assignments = []
if data:
ass_object = Assignment().from_dict(data)
assignments.append(ass_object)

return assignments

def get_active_assignments(self) -> List[Assignment]:
response = self.get("assignments/active")
Expand Down Expand Up @@ -301,8 +299,8 @@ def get_interfaces(self) -> Response:
def update_interface(self, hostname, data) -> Response:
return self.patch(os.path.join("interfaces", hostname), data)

def remove_interface(self, interface_id) -> Response:
return self.delete(os.path.join("interfaces", interface_id))
def remove_interface(self, hostname, if_name) -> Response:
return self.delete(os.path.join("interfaces", hostname, if_name))

def create_interface(self, hostname, data) -> Response:
return self.post(os.path.join("interfaces", hostname), data)
Expand Down Expand Up @@ -341,15 +339,21 @@ def get_vlans(self) -> List[Vlan]:
def get_vlan(self, vlan_id) -> Response:
return self.get(os.path.join("vlans", str(vlan_id)))

def update_vlan(self, vlan_id, data) -> Response:
def update_vlan(self, vlan_id, data: dict) -> Response:
return self.patch(os.path.join("vlans", str(vlan_id)), data)

# Processor
def create_vlan(self, data) -> Response:
def create_vlan(self, data: dict) -> Response:
return self.post("vlans", data)

def get_summary(self) -> Response:
return self.get("clouds/summary")
def get_summary(self, data: dict) -> Response:
url_params = url_parse.urlencode(data)
endpoint = os.path.join("clouds", "summary")
url = f"{endpoint}"
if data:
url = f"{endpoint}?{url_params}"
response = self.get(url)
return response

def get_version(self) -> Response:
return self.get("version")
29 changes: 13 additions & 16 deletions quads/server/blueprints/assignments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from quads.server.dao.baseDao import EntryNotFound, InvalidArgument, BaseDao
from quads.server.dao.cloud import CloudDao
from quads.server.dao.vlan import VlanDao
from quads.server.models import Assignment, Notification, db

assignment_bp = Blueprint("assignments", __name__)

Expand Down Expand Up @@ -49,7 +48,7 @@ def get_assignment(assignment_id: str) -> Response:
:param assignment_id: Get the assignment from the database
:return: The assignment as a json object
"""
_assignment = AssignmentDao.get_assignment(assignment_id)
_assignment = AssignmentDao.get_assignment(int(assignment_id))
if not _assignment:
response = {
"status_code": 400,
Expand Down Expand Up @@ -117,7 +116,6 @@ def create_assignment() -> Response:
"""
data = request.get_json()

notification = Notification()
_cloud = None
_vlan = None
cloud_name = data.get("cloud")
Expand Down Expand Up @@ -175,19 +173,18 @@ def create_assignment() -> Response:
}
return make_response(jsonify(response), 400)

_assignment_obj = Assignment(
description=description,
owner=owner,
ticket=ticket,
qinq=qinq,
wipe=wipe,
ccuser=cc_user,
vlan=_vlan,
cloud=_cloud,
notification=notification,
)
db.session.add(_assignment_obj)
BaseDao.safe_commit()
kwargs = {
"description": description,
"owner": owner,
"ticket": ticket,
"qinq": qinq,
"wipe": wipe,
"ccuser": cc_user,
"cloud": cloud_name,
}
if _vlan:
kwargs["vlan_id"] = int(vlan)
_assignment_obj = AssignmentDao.create_assignment(**kwargs)
return jsonify(_assignment_obj.as_dict())


Expand Down
6 changes: 2 additions & 4 deletions quads/server/blueprints/available.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def get_available() -> Response:
if ScheduleDao.is_host_available(host.name, _start, _end):
if _cloud:
_sched_cloud = ScheduleDao.get_current_schedule(host=host)
_sched_cloud = (
_sched_cloud[0].assignment.cloud.name if _sched_cloud else None
)
_sched_cloud = _sched_cloud[0].assignment.cloud.name if _sched_cloud else None
if _cloud != _sched_cloud:
continue
available.append(host.name)
Expand All @@ -69,7 +67,7 @@ def is_available(hostname) -> Response:
_params = request.args.to_dict()
_start = _end = datetime.now()
if _params.get("start"):
_start = datetime.strptime(_params.get("start"), "%Y-%m-%dT%H:%M")
_start = datetime.strptime(_params.get("start"), "%Y-%m-%dT%H:%M") + timedelta(minutes=1)
if _params.get("end"):
_end = datetime.strptime(_params.get("end"), "%Y-%m-%dT%H:%M")
if _start > _end:
Expand Down
13 changes: 9 additions & 4 deletions quads/server/blueprints/clouds.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ def get_cloud(cloud: str) -> Response:
:return: A response object that contains the json representation of the cloud
"""
_cloud = CloudDao.get_cloud(cloud)
return jsonify(_cloud.as_dict() if _cloud else {})
if not _cloud:
response = {
"status_code": 400,
"error": "Bad Request",
"message": f"Cloud not found: {cloud}",
}
return make_response(jsonify(response), 400)
return jsonify(_cloud.as_dict())


@cloud_bp.route("/")
Expand Down Expand Up @@ -136,9 +143,7 @@ def get_summary() -> Response:
count = len(hosts)
else:
date = (
datetime.strptime(_date, "%Y-%m-%dT%H:%M:%S")
if _date
else datetime.now()
datetime.strptime(_date, "%Y-%m-%dT%H:%M") if _date else datetime.now()
)
schedules = ScheduleDao.get_current_schedule(cloud=_cloud, date=date)
count = len(schedules)
Expand Down
4 changes: 4 additions & 0 deletions quads/server/blueprints/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def update_host(hostname: str) -> Response:
cloud_name = data.get("cloud")
default_cloud = data.get("default_cloud")
host_type = data.get("host_type")
model = data.get("model")
broken = data.get("broken")
retired = data.get("retired")

Expand Down Expand Up @@ -87,6 +88,9 @@ def update_host(hostname: str) -> Response:
if host_type:
_host.host_type = host_type

if model:
_host.model = model.upper()

if isinstance(broken, bool):
_host.broken = broken

Expand Down

0 comments on commit 1f58eb5

Please sign in to comment.