From 4b25bf0a85cb65c0c9da2678cb246662c8e6e59c Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Thu, 16 Dec 2021 17:43:47 +0100 Subject: [PATCH] mgr/cephadm: SNMP: use of python3 enums Little reason to duplicate things ourselves Signed-off-by: Sebastian Wagner (cherry picked from commit 0039accb2caedf99166b88cc5b75736b6a7fd5c2) Conflicts: src/pybind/mgr/orchestrator/module.py src/python-common/ceph/deployment/service_spec.py src/python-common/ceph/tests/test_service_spec.py --- src/pybind/mgr/cephadm/tests/test_spec.py | 2 +- src/pybind/mgr/orchestrator/module.py | 17 ++-- .../ceph/deployment/service_spec.py | 78 ++++++++++--------- .../ceph/tests/test_service_spec.py | 17 ++++ 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/pybind/mgr/cephadm/tests/test_spec.py b/src/pybind/mgr/cephadm/tests/test_spec.py index 84fd927386877..2b113dfd98e13 100644 --- a/src/pybind/mgr/cephadm/tests/test_spec.py +++ b/src/pybind/mgr/cephadm/tests/test_spec.py @@ -719,7 +719,7 @@ auth_protocol: SHA privacy_protocol: weewah snmp_destination: 192.168.1.42:162 snmp_version: V3 -"""), 'privacy_protocol unsupported. Must be one of AES, DES'), +"""), 'privacy_protocol unsupported. Must be one of DES, AES'), (YAMLdoc(""" --- service_type: snmp-gateway diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index 7d04c2d4e743e..4176a7adeeed5 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -11,7 +11,7 @@ from prettytable import PrettyTable from ceph.deployment.inventory import Device from ceph.deployment.drive_group import DriveGroupSpec, DeviceSelection from ceph.deployment.service_spec import PlacementSpec, ServiceSpec, \ - SNMPGatewaySpec, SNMPVersion, SNMPAuthType, SNMPPrivacyType + SNMPGatewaySpec from ceph.deployment.hostspec import SpecValidationError from ceph.utils import datetime_now @@ -1161,12 +1161,12 @@ Usage: @_cli_write_command('orch apply snmp-gateway') def _apply_snmp_gateway(self, - snmp_version: SNMPVersion, + snmp_version: SNMPGatewaySpec.SNMPVersion, destination: str, port: int = 9464, engine_id: Optional[str] = None, - auth_protocol: Optional[SNMPAuthType] = None, - privacy_protocol: Optional[SNMPPrivacyType] = None, + auth_protocol: Optional[SNMPGatewaySpec.SNMPAuthType] = None, + privacy_protocol: Optional[SNMPGatewaySpec.SNMPPrivacyType] = None, placement: Optional[str] = None, unmanaged: bool = False, dry_run: bool = False, @@ -1185,17 +1185,14 @@ Usage: except (OSError, yaml.YAMLError): raise OrchestratorValidationError('credentials file must be valid YAML') - auth = None if not auth_protocol else auth_protocol.value - priv = None if not privacy_protocol else privacy_protocol.value - spec = SNMPGatewaySpec( - snmp_version=snmp_version.value, + snmp_version=snmp_version, port=port, credentials=credentials, snmp_destination=destination, engine_id=engine_id, - auth_protocol=auth, - privacy_protocol=priv, + auth_protocol=auth_protocol, + privacy_protocol=privacy_protocol, placement=PlacementSpec.from_string(placement), unmanaged=unmanaged, preview_only=dry_run diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index 124b48889795a..fea2ec766fead 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -16,20 +16,6 @@ from ceph.utils import is_hex ServiceSpecT = TypeVar('ServiceSpecT', bound='ServiceSpec') FuncT = TypeVar('FuncT', bound=Callable) -class SNMPVersion(enum.Enum): - V2c = 'V2c' - V3 = 'V3' - - -class SNMPAuthType(enum.Enum): - MD5 = 'MD5' - SHA = 'SHA' - - -class SNMPPrivacyType(enum.Enum): - DES = 'DES' - AES = 'AES' - def assert_valid_host(name: str) -> None: p = re.compile('^[a-zA-Z0-9-]+$') @@ -1151,6 +1137,27 @@ yaml.add_representer(GrafanaSpec, ServiceSpec.yaml_representer) class SNMPGatewaySpec(ServiceSpec): + class SNMPVersion(str, enum.Enum): + V2c = 'V2c' + V3 = 'V3' + + def to_json(self) -> str: + return self.value + + class SNMPAuthType(str, enum.Enum): + MD5 = 'MD5' + SHA = 'SHA' + + def to_json(self) -> str: + return self.value + + class SNMPPrivacyType(str, enum.Enum): + DES = 'DES' + AES = 'AES' + + def to_json(self) -> str: + return self.value + valid_destination_types = [ 'Name:Port', 'IPv4:Port' @@ -1158,12 +1165,12 @@ class SNMPGatewaySpec(ServiceSpec): def __init__(self, service_type: str = 'snmp-gateway', - snmp_version: Optional[str] = None, + snmp_version: Optional[SNMPVersion] = None, snmp_destination: str = '', credentials: Dict[str, str] = {}, engine_id: Optional[str] = None, - auth_protocol: Optional[str] = None, - privacy_protocol: Optional[str] = None, + auth_protocol: Optional[SNMPAuthType] = None, + privacy_protocol: Optional[SNMPPrivacyType] = None, placement: Optional[PlacementSpec] = None, unmanaged: bool = False, preview_only: bool = False, @@ -1186,6 +1193,25 @@ class SNMPGatewaySpec(ServiceSpec): self.auth_protocol = auth_protocol self.privacy_protocol = privacy_protocol + @classmethod + def _from_json_impl(cls, json_spec: dict) -> 'SNMPGatewaySpec': + + cpy = json_spec.copy() + types = [ + ('snmp_version', SNMPGatewaySpec.SNMPVersion), + ('auth_protocol', SNMPGatewaySpec.SNMPAuthType), + ('privacy_protocol', SNMPGatewaySpec.SNMPPrivacyType), + ] + for d in cpy, cpy.get('spec', {}): + for key, enum_cls in types: + try: + if key in d: + d[key] = enum_cls(d[key]) + except ValueError: + raise SpecValidationError(f'{key} unsupported. Must be one of ' + f'{", ".join(enum_cls)}') + return super(SNMPGatewaySpec, cls)._from_json_impl(cpy) + @property def ports(self) -> List[int]: return [self.port] @@ -1196,14 +1222,6 @@ class SNMPGatewaySpec(ServiceSpec): def validate(self) -> None: super(SNMPGatewaySpec, self).validate() - def _check_type(name: str, value: Optional[str], options: List[str]) -> None: - if not value: - return - if value not in options: - raise SpecValidationError( - f'{name} unsupported. Must be one of {", ".join(sorted(options))}' - ) - if not self.credentials: raise SpecValidationError( 'Missing authentication information (credentials). ' @@ -1214,16 +1232,6 @@ class SNMPGatewaySpec(ServiceSpec): 'Missing SNMP version (snmp_version)' ) - _check_type('snmp_version', - self.snmp_version, - list(set(opt.value for opt in SNMPVersion))) - _check_type('auth_protocol', - self.auth_protocol, - list(set(opt.value for opt in SNMPAuthType))) - _check_type('privacy_protocol', - self.privacy_protocol, - list(set(opt.value for opt in SNMPPrivacyType))) - creds_requirement = { 'V2c': ['snmp_community'], 'V3': ['snmp_v3_auth_username', 'snmp_v3_auth_password'] diff --git a/src/python-common/ceph/tests/test_service_spec.py b/src/python-common/ceph/tests/test_service_spec.py index 04225e2e1a231..93ae36b49a96a 100644 --- a/src/python-common/ceph/tests/test_service_spec.py +++ b/src/python-common/ceph/tests/test_service_spec.py @@ -561,6 +561,23 @@ service_id: foo placement: count_per_host: 0 """, "count-per-host must be >= 1",), + (""" +service_type: snmp-gateway +service_name: snmp-gateway +placement: + count: 1 +spec: + credentials: + snmp_v3_auth_password: mypassword + snmp_v3_auth_username: myuser + snmp_v3_priv_password: mysecret + port: 9464 + engine_id: 8000c53f0000000000 + privacy_protocol: WEIRD + snmp_destination: 192.168.122.1:162 + auth_protocol: BIZARRE + snmp_version: V3 +""", "auth_protocol unsupported. Must be one of MD5, SHA"), ]) def test_service_spec_validation_error(y, error_match): data = yaml.safe_load(y) -- 2.39.5