from ...adapters import ( DNS_RECORDS, Capability, CapabilityOperation, DiscoveredObject, NormalizedObject, PlanAction, PlanOperation, ) from ...models import DesiredRecord from ..providers.verizon import Plugin as VerizonProvider class Plugin: name = "verizon" capabilities = frozenset( { Capability( DNS_RECORDS, CapabilityOperation.DISCOVER, ), Capability( DNS_RECORDS, CapabilityOperation.APPLY, ), Capability( DNS_RECORDS, CapabilityOperation.VERIFY, ), } ) def __init__(self, config, endpoint): if endpoint.plugin != self.name: raise RuntimeError( "Verizon adapter requires a verizon endpoint" ) self.config = config self.endpoint = endpoint self.provider = VerizonProvider(config, endpoint) def connect(self) -> None: self.provider.connect() def discover( self, object_type: str, ) -> list[DiscoveredObject]: if object_type != DNS_RECORDS: raise RuntimeError( f"{self.endpoint.identity} cannot discover " f"object type {object_type!r}" ) discovered: list[DiscoveredObject] = [] records = sorted( self.provider.list_records().values(), key=lambda record: record.hostname.lower(), ) for record in records: slot = record.metadata.get("slot") if isinstance(slot, bool) or not isinstance(slot, int): raise RuntimeError( f"Verizon DNS record {record.hostname!r} " "has no valid endpoint-local slot" ) discovered.append( DiscoveredObject( object_type=DNS_RECORDS, local_key=str(slot), payload={ "hostname": record.hostname, "ip": record.ip, "record_type": record.record_type, }, ) ) return discovered def _desired_record( self, object: NormalizedObject, ) -> DesiredRecord: if object.object_type != DNS_RECORDS: raise RuntimeError( f"{self.endpoint.identity} cannot handle " f"object type {object.object_type!r}" ) payload = object.payload hostname = str(payload.get("hostname") or "") if hostname != object.logical_key: raise RuntimeError( "DNS object hostname does not match its logical key" ) record_type = str( payload.get("record_type") or "" ).upper() if record_type != "A": raise RuntimeError( f"Verizon DNS does not support {record_type!r} records" ) wildcard = payload.get("wildcard") is True if wildcard: raise RuntimeError( "Verizon DNS does not support wildcard records" ) return DesiredRecord( hostname=hostname, ip=str(payload.get("ip") or ""), record_type=record_type, wildcard=False, ) def apply(self, action: PlanAction) -> str | None: if action.endpoint_id != self.endpoint.identity: raise RuntimeError( "Plan action belongs to a different endpoint" ) if action.operation in { PlanOperation.NOOP, PlanOperation.SKIP, }: return None if action.desired is None: raise RuntimeError( "Verizon apply action requires desired state" ) record = self._desired_record(action.desired) if action.operation == PlanOperation.CREATE: return self.provider.add_record(record) if action.operation == PlanOperation.UPDATE: return self.provider.update_record(record) raise RuntimeError( f"Verizon adapter does not support " f"{action.operation.value} actions" ) def verify(self, object: NormalizedObject) -> bool: return self.provider.verify_record( self._desired_record(object) )