Skip to content

Commit 3898a3a

Browse files
authored
Merge pull request #117 from 2ndWatch/add/report_function
CLI helper functions to audit CloudEndure
2 parents b589875 + c6dab32 commit 3898a3a

File tree

4 files changed

+73
-3
lines changed

4 files changed

+73
-3
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SHA1 := $$(git log -1 --pretty=%h)
88
CURRENT_BRANCH := $$(git symbolic-ref -q --short HEAD)
99
LATEST_TAG := ${REPO_NAME}:latest
1010
GIT_TAG := ${REPO_NAME}:${SHA1}
11-
VERSION := v0.2.8
11+
VERSION := v0.2.9
1212

1313
info: ## Show information about the current git state.
1414
@echo "Github Project: https://github.com/${REPO_NAME}\nCurrent Branch: ${CURRENT_BRANCH}\nSHA1: ${SHA1}\n"

cloudendure/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.2.8"
1+
__version__ = "0.2.9"

cloudendure/cloudendure.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import json
99
import os
1010
import pprint
11+
from datetime import datetime, timezone
1112
from typing import Any, Dict, List
1213

1314
import boto3
@@ -446,6 +447,75 @@ def check_licenses(self) -> Dict[str, Any]:
446447

447448
return response_dict
448449

450+
def get_machine_sync_details(self) -> List[Any]:
451+
"""Checks CloudEndure Project inventory and returns register machine's
452+
replication state.
453+
"""
454+
response_list: List[Any] = []
455+
print(f"INFO: Retreiving sync status for all machines in Project: ({self.project_name})")
456+
machines_response: Response = self.api.api_call(f"projects/{self.project_id}/machines")
457+
if not machines_response.ok:
458+
print(f"ERROR: API response did not return a 2XX status; Returned {machines_response.status_code} ...")
459+
return {}
460+
ce_project_inventory = json.loads(machines_response.text).get("items", [])
461+
for _query_value in ce_project_inventory:
462+
machine_name: str = _query_value["sourceProperties"]["name"]
463+
sync_details: Dict[str, Any] = {
464+
"machine_name": machine_name,
465+
"in_inventory": _query_value["isAgentInstalled"],
466+
"replication_status": _query_value["replicationStatus"],
467+
"last_seen_utc": _query_value["replicationInfo"]["lastSeenDateTime"],
468+
"total_storage_bytes": _query_value["replicationInfo"]["totalStorageBytes"],
469+
"replicated_storage_bytes": _query_value["replicationInfo"]["replicatedStorageBytes"],
470+
"rescanned_storage_bytes": 0,
471+
"backlogged_storage_bytes": _query_value["replicationInfo"]["backloggedStorageBytes"],
472+
}
473+
if "rescannedStorageBytes" in _query_value["replicationInfo"]:
474+
sync_details["recanned_storage_bytes"] = _query_value["replicationInfo"]["rescannedStorageBytes"]
475+
response_list.append(sync_details)
476+
# Project is still printing to console as a convention; Emitting an
477+
# output to stdout for interactive usage
478+
return response_list
479+
480+
def inspect_ce_project(self, check_type: str) -> List[Any]:
481+
if not check_type:
482+
print(
483+
f"ERROR: Unknown check_type of '{check_type}'; Please use 'not_synced', 'not_started', or 'not_current' ..."
484+
)
485+
return
486+
result: List[Any] = []
487+
sync_report: List[Any] = self.get_machine_sync_details()
488+
print(f"INFO: Using check '{check_type}' on Project: ({self.project_name})")
489+
inspector = getattr(self, check_type)
490+
for item in sync_report:
491+
mcheck = inspector(machine=item)
492+
if mcheck:
493+
result.append(item)
494+
print(f"INFO: Check '{check_type}' completed; {len(result)} machines matched in Project: ({self.project_name})")
495+
return result
496+
497+
def not_synced(self, machine) -> bool:
498+
if machine["backlogged_storage_bytes"] > 0 or machine["rescanned_storage_bytes"] > 0:
499+
return True
500+
else:
501+
return False
502+
503+
def not_started(self, machine) -> bool:
504+
if machine["replication_status"] == "STARTED":
505+
return False
506+
else:
507+
return True
508+
509+
def not_current(self, machine, delta_seconds: int = 86400) -> bool:
510+
now: datetime = datetime.now(timezone.utc)
511+
machine_last_seen: datetime = datetime.fromisoformat(machine["last_seen_utc"])
512+
last_seen_delta: datetime = now - machine_last_seen
513+
# If you're exceeding the size of int, you have bigger problems
514+
if int(last_seen_delta.total_seconds()) >= delta_seconds:
515+
return True
516+
else:
517+
return False
518+
449519
def update_blueprint(self) -> bool:
450520
"""Update the blueprint associated with the specified machines."""
451521
print(f"Updating CloudEndure Blueprints - Name: ({self.project_name}) - Dry Run: ({self.dry_run})")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "cloudendure"
3-
version = "0.2.8"
3+
version = "0.2.9"
44
description = "Python wrapper and CLI for CloudEndure"
55
authors = ["Mark Beacom <mark@markbeacom.com>", "Tom Warnock <twarnock@2ndwatch.com>"]
66
maintainers = ["Evan Lucchesi <evan@2ndwatch.com>", "Nick Selpa <nselpa@2ndwatch.com>"]

0 commit comments

Comments
 (0)