diff --git a/camayoc/qcs_models.py b/camayoc/qcs_models.py index 5aa10afb..6fe2ae27 100644 --- a/camayoc/qcs_models.py +++ b/camayoc/qcs_models.py @@ -136,19 +136,19 @@ def delete(self, **kwargs): return self.client.delete(self.path(), **kwargs) -class HostCredential(QCSObject): +class Credential(QCSObject): """A class to aid in CRUD tests of Host Credentials on the QCS server. - Host credentials can be created by instantiating a HostCredential + Host credentials can be created by instantiating a Credential object. A unique name and username are provided by default. In order to create a valid host credential you must specify either a password or ssh_keyfile. Example:: >>> from camayoc import api - >>> from camayoc.qcs_models import HostCredential + >>> from camayoc.qcs_models import Credential >>> client = api.QCSClient() - >>> cred = HostCredential(password='foo') + >>> cred = Credential(cred_type='network', password='foo') >>> # The create method automatically sets the credential's `_id` >>> cred.create() >>> actual_cred = cred.read().json() @@ -163,13 +163,14 @@ def __init__( password=None, ssh_keyfile=None, sudo_password=None, + cred_type=None, _id=None): """Create a host credential with given data. If no arguments are passed, then a api.Client will be initialized and a uuid4 generated for the name and username. - For a HostCredential to be successfully created on the QCS server, + For a Credential to be successfully created on the QCS server, a password XOR a ssh_keyfile must be provided. """ super().__init__(client=client, _id=_id) @@ -179,23 +180,24 @@ def __init__( self.password = password self.ssh_keyfile = ssh_keyfile self.sudo_password = sudo_password + self.cred_type = cred_type def equivalent(self, other): """Return true if both objects are equal. - :param other: This can be either another HostCredential or a dictionary + :param other: This can be either another Credential or a dictionary or json object returned from the QCS server (or crafted by hand.) - If `other` is a HostCredential instance, the two object's fields() + If `other` is a Credential instance, the two object's fields() will be compared. Otherwise, we expect the password to have been masked by the server. """ - if isinstance(other, HostCredential): + if isinstance(other, Credential): return self.fields() == other.fields() if not isinstance(other, dict): raise TypeError( - 'Objects of type HostCredential can only be compared to' - 'HostCredential objects or dictionaries.' + 'Objects of type Credential can only be compared to' + 'Credential objects or dictionaries.' ) password_matcher = re.compile(MASKED_PASSWORD_OUTPUT) @@ -209,20 +211,20 @@ def equivalent(self, other): return True -class NetworkProfile(QCSObject): +class Source(QCSObject): """A class to aid in CRUD test cases for network profiles. Network profiles can be created on the quipucords server by - instantiating a NetworkProfile object. A unique name and username are + instantiating a Source object. A unique name and username are provided by default. In order to create a valid network profile, you must specify at least one existing host credential and one host. Example:: - >>> from camayoc.qcs_models import NetworkProfile + >>> from camayoc.qcs_models import Source >>> - >>> hostcred = HostCredential(password='foo') + >>> hostcred = Credential(cred_type='network',password='foo') >>> hostcred.create() - >>> netprof = NetworkProfile(hosts=['0.0.0.0'], + >>> netprof = Source( source_type='network', hosts=['0.0.0.0'], credential_ids=[hostcred._id]) >>> netprof.create() >>> actual_prof = netprof.read().json() @@ -238,8 +240,9 @@ def __init__( hosts=None, ssh_port=22, credential_ids=None, + source_type=None, _id=None): - """Iniitalize a NetworkProfile object with given data. + """Iniitalize a Source object with given data. If no ssh_port is supplied, it will be set to 22 by default. A uuid4 name and api.Client are also supplied if none are provided. @@ -250,24 +253,25 @@ def __init__( self.hosts = hosts self.ssh_port = ssh_port self.credentials = credential_ids + self.source_type = source_type def equivalent(self, other): """Return true if both objects are equivalent. - :param other: This can be either another NetworkProfile or a dictionary + :param other: This can be either another Source or a dictionary or json object returned from the QCS server (or crafted by hand.) - If `other` is a NetworkProfile instance, the two object's fields() + If `other` is a Source instance, the two object's fields() will be compared. Otherwise, we must extract the credential id's from the json returned by the server into a list, because that is all the data that we use to generate the network profile. """ - if isinstance(other, NetworkProfile): + if isinstance(other, Source): return self.fields() == other.fields() if not isinstance(other, dict): raise TypeError( - 'Objects of type NetworkProfile can only be compared to' - 'NetworkProfiles objects or dictionaries.' + 'Objects of type Source can only be compared to' + 'Sources objects or dictionaries.' ) for key, value in self.fields().items(): @@ -275,7 +279,7 @@ def equivalent(self, other): other_creds = other.get('credentials') cred_ids = [] # the server returns a list of dictionaries - # one for each credential associated with the NetworkProfile + # one for each credential associated with the Source # we extract from this all the id's and then compare it with # the list of id's we used to create the Network Profile for cred in other_creds: @@ -294,13 +298,13 @@ class Scan(QCSObject): Scan jobs can be created on the quipucords server by instantiating a Scan object and then calling its create() method. - The id of an existing NetworkProfile is necessary to create a scan + The id of an existing Source is necessary to create a scan job. Example:: - >>> hostcred = HostCredential(password='foo') + >>> hostcred = Credential(cred_type='network', password='foo') >>> hostcred.create() - >>> netprof = NetworkProfile(hosts=['0.0.0.0'], + >>> netprof = Source( source_type='network', hosts=['0.0.0.0'], credential_ids=[hostcred._id]) >>> netprof.create() >>> scan = Scan(profile_id=netprof._id) diff --git a/camayoc/tests/qcs/test_credentials.py b/camayoc/tests/qcs/test_credentials.py index ac558315..d3af81e6 100644 --- a/camayoc/tests/qcs/test_credentials.py +++ b/camayoc/tests/qcs/test_credentials.py @@ -13,7 +13,7 @@ from pathlib import Path from camayoc import api -from camayoc.qcs_models import HostCredential +from camayoc.qcs_models import Credential from camayoc.utils import uuid4 from camayoc.tests.qcs.utils import assert_matches_server @@ -26,7 +26,10 @@ def test_create_with_password(shared_client, cleanup): :steps: Send POST with necessary data to documented api endpoint. :expectedresults: A new host credential entry is created with the data. """ - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the credential to the list to destroy after the test is done cleanup.append(cred) @@ -45,7 +48,10 @@ def test_update_username(shared_client, cleanup): 3) Confirm host credential has been updated. :expectedresults: The host credential is updated. """ - cred = HostCredential(shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the id to the list to destroy after the test is done cleanup.append(cred) @@ -70,7 +76,10 @@ def test_update_password_to_sshkeyfile( 3) Confirm host credential has been updated. :expectedresults: The host credential is updated. """ - cred = HostCredential(shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the id to the list to destroy after the test is done cleanup.append(cred) @@ -102,10 +111,10 @@ def test_update_sshkey_to_password( ssh_keyfile = Path(uuid4()) ssh_keyfile.touch() - cred = HostCredential( - shared_client, - ssh_keyfile=str(ssh_keyfile.resolve()), - ) + cred = Credential(cred_type='network', + client=shared_client, + ssh_keyfile=str(ssh_keyfile.resolve()), + ) cred.create() # add the id to the list to destroy after the test is done cleanup.append(cred) @@ -136,10 +145,10 @@ def test_negative_update_to_invalid( ssh_keyfile = Path(uuid4()) ssh_keyfile.touch() - cred = HostCredential( - shared_client, - ssh_keyfile=str(ssh_keyfile.resolve()), - ) + cred = Credential(cred_type='network', + client=shared_client, + ssh_keyfile=str(ssh_keyfile.resolve()), + ) cred.create() # add the id to the list to destroy after the test is done cleanup.append(cred) @@ -183,10 +192,10 @@ def test_create_with_sshkey( ssh_keyfile = Path(uuid4()) ssh_keyfile.touch() - cred = HostCredential( - shared_client, - ssh_keyfile=str(ssh_keyfile.resolve()), - ) + cred = Credential(cred_type='network', + client=shared_client, + ssh_keyfile=str(ssh_keyfile.resolve()), + ) cred.create() # add the id to the list to destroy after the test is done cleanup.append(cred) @@ -207,11 +216,11 @@ def test_negative_create_key_and_pass(cleanup, isolated_filesystem): ssh_keyfile.touch() client = api.Client(api.echo_handler) - cred = HostCredential( - client, - ssh_keyfile=str(ssh_keyfile.resolve()), - password=uuid4(), - ) + cred = Credential(cred_type='network', + client=client, + ssh_keyfile=str(ssh_keyfile.resolve()), + password=uuid4(), + ) response = cred.create() assert response.status_code == 400 assert cred._id is None @@ -228,11 +237,11 @@ def test_negative_create_no_name(cleanup): :expectedresults: Error is thrown and no new host credential is created. """ client = api.Client(api.echo_handler) - cred = HostCredential( - client, - username='', - password=uuid4(), - ) + cred = Credential(cred_type='network', + client=client, + username='', + password=uuid4(), + ) response = cred.create() assert response.status_code == 400 assert cred._id is None @@ -249,7 +258,7 @@ def test_negative_create_no_key_or_pass(cleanup): :expectedresults: Error is thrown and no new host credential is created. """ client = api.Client(api.echo_handler) - cred = HostCredential(client) + cred = Credential(cred_type='network', client=client) response = cred.create() assert response.status_code == 400 assert cred._id is None @@ -270,14 +279,17 @@ def test_read_all(shared_client, cleanup): """ host_credentials = [] for _ in range(random.randint(2, 5)): - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the credential to the list to destroy after the test is done cleanup.append(cred) assert_matches_server(cred) host_credentials.append(cred) - remote_host_credentials = HostCredential().list().json() + remote_host_credentials = Credential(cred_type='network', ).list().json() for local, remote in zip(host_credentials, remote_host_credentials): local.equivalent(remote) @@ -297,7 +309,10 @@ def test_read_indv(shared_client, cleanup): """ host_credentials = [] for _ in range(random.randint(2, 5)): - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the credential to the list to destroy after the test is done cleanup.append(cred) @@ -305,7 +320,9 @@ def test_read_indv(shared_client, cleanup): host_credentials.append(cred) for host_credential in host_credentials: - remote = HostCredential(_id=host_credential._id).read().json() + remote = Credential( + cred_type='network', + _id=host_credential._id).read().json() host_credential.equivalent(remote) @@ -326,7 +343,10 @@ def test_delete(shared_client, cleanup): """ host_credentials = [] for _ in range(random.randint(2, 5)): - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() # add the credential to the list to destroy after the test is done cleanup.append(cred) @@ -340,5 +360,7 @@ def test_delete(shared_client, cleanup): selected.delete() for host_credential in host_credentials: - remote = HostCredential(_id=host_credential._id).read().json() + remote = Credential( + cred_type='network', + _id=host_credential._id).read().json() host_credential.equivalent(remote) diff --git a/camayoc/tests/qcs/test_profiles.py b/camayoc/tests/qcs/test_profiles.py index 079908e3..e60f0a78 100644 --- a/camayoc/tests/qcs/test_profiles.py +++ b/camayoc/tests/qcs/test_profiles.py @@ -16,8 +16,8 @@ from camayoc import api from camayoc.utils import uuid4 -from camayoc.qcs_models import HostCredential -from camayoc.qcs_models import NetworkProfile +from camayoc.qcs_models import Credential +from camayoc.qcs_models import Source from camayoc.tests.qcs.utils import ( assert_matches_server, assert_profile_update_fails, @@ -39,13 +39,16 @@ def test_create(shared_client, cleanup, scan_host): credential to the profile endpoint. :expectedresults: A new network profile entry is created with the data. """ - cred = HostCredential(client=shared_client, password=uuid4()) - cred.create() - profile = NetworkProfile( + cred = Credential( + cred_type='network', client=shared_client, - hosts=[scan_host], - credential_ids=[cred._id], - ) + password=uuid4()) + cred.create() + profile = Source(source_type='network', + client=shared_client, + hosts=[scan_host], + credential_ids=[cred._id], + ) profile.create() # add the ids to the lists to destroy after the test is done cleanup.extend([cred, profile]) @@ -67,19 +70,22 @@ def test_update(shared_client, cleanup, scan_host): the data :expectedresults: The newtork profile entry is created and updated. """ - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() cleanup.append(cred) - profile = NetworkProfile( - client=shared_client, - hosts=[scan_host], - credential_ids=[cred._id], - ) + profile = Source(source_type='network', + client=shared_client, + hosts=[scan_host], + credential_ids=[cred._id], + ) profile.create() cleanup.append(profile) assert_matches_server(profile) profile.hosts.append('0.0.0.0') - cred2 = HostCredential(password=uuid4()) + cred2 = Credential(cred_type='network', password=uuid4()) cred2.create() cleanup.append(cred2) profile.credentials.append(cred2._id) @@ -165,14 +171,17 @@ def test_negative_create_missing_data(cleanup, shared_client, field): c) credential id's :expectedresults: Error is thrown and no new profile is created. """ - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() cleanup.append(cred) - profile = NetworkProfile( - client=api.Client(response_handler=api.echo_handler), - hosts=['localhost'], - credential_ids=[cred._id], - ) + profile = Source(source_type='network', + client=api.Client(response_handler=api.echo_handler), + hosts=['localhost'], + credential_ids=[cred._id], + ) # remove field from payload delattr(profile, field) @@ -211,15 +220,18 @@ def test_negative_create_invalid_data( c) name :expectedresults: Error is thrown and no new profile is created. """ - cred = HostCredential(client=shared_client, password=uuid4()) + cred = Credential( + cred_type='network', + client=shared_client, + password=uuid4()) cred.create() cleanup.append(cred) data['credential_ids'] = cred._id if not data['credential_ids'] else [-1] - profile = NetworkProfile( - client=api.Client(response_handler=api.echo_handler), - # unpack parametrized arguments - **data - ) + profile = Source(source_type='network', + client=api.Client(response_handler=api.echo_handler), + # unpack parametrized arguments + **data + ) create_response = profile.create() assert create_response.status_code == 400 @@ -241,7 +253,7 @@ def test_read_all(cleanup, shared_client): for _ in range(random.randint(2, 20)): # gen_valid_profs will take care of cleanup created_profs.append(gen_valid_profile(cleanup, 'localhost')) - server_profs = NetworkProfile().list().json() + server_profs = Source().list().json() for sp in server_profs: for i, cp in enumerate(created_profs): if sp['id'] == cp.fields()['id'] and cp.equivalent(sp): diff --git a/camayoc/tests/qcs/test_scan.py b/camayoc/tests/qcs/test_scan.py index 17131a81..9384db2e 100644 --- a/camayoc/tests/qcs/test_scan.py +++ b/camayoc/tests/qcs/test_scan.py @@ -16,8 +16,8 @@ from camayoc import config from camayoc.exceptions import ConfigFileNotFoundError from camayoc.qcs_models import ( - HostCredential, - NetworkProfile, + Credential, + Source, Scan, ) @@ -45,7 +45,7 @@ def wait_until_complete(scan, timeout=120): 'You have called wait_until_complete() on a scan ' 'that is paused or cancelled, and this could hang forever.' ' This exception has been raised instead.' - ) + ) while ( not scan.status() or not scan.status() in [ 'failed', @@ -77,20 +77,20 @@ def test_create(shared_client, cleanup, profile, isolated_filesystem): auth_ids = [] for auth in cfg['qcs'].get('auths'): if auth['name'] in profile['auths']: - cred = HostCredential( - client=shared_client, - ssh_keyfile=auth['sshkeyfile'], - username=auth['username'], - ) + cred = Credential(cred_type='network', + client=shared_client, + ssh_keyfile=auth['sshkeyfile'], + username=auth['username'], + ) cred.create() cleanup.append(cred) auth_ids.append(cred._id) - netprof = NetworkProfile( - client=shared_client, - hosts=profile['hosts'], - credential_ids=auth_ids, - ) + netprof = Source(source_type='network', + client=shared_client, + hosts=profile['hosts'], + credential_ids=auth_ids, + ) netprof.create() cleanup.append(netprof) scan = Scan(source_id=netprof._id) diff --git a/camayoc/tests/qcs/utils.py b/camayoc/tests/qcs/utils.py index eb449470..99274f35 100644 --- a/camayoc/tests/qcs/utils.py +++ b/camayoc/tests/qcs/utils.py @@ -2,7 +2,7 @@ from camayoc import api from camayoc.utils import uuid4 -from camayoc.qcs_models import HostCredential, NetworkProfile +from camayoc.qcs_models import Credential, Source def assert_matches_server(qcsobject): @@ -41,14 +41,14 @@ def assert_profile_update_fails(original_data, profile): def gen_valid_profile(cleanup, host): """Create valid profile and return it with echo_handler.""" client = api.Client() - cred = HostCredential(client=client, password=uuid4()) + cred = Credential(cred_type='network', client=client, password=uuid4()) cred.create() cleanup.append(cred) - profile = NetworkProfile( - hosts=[host], - credential_ids=[cred._id], - client=client, - ) + profile = Source(source_type='network', + hosts=[host], + credential_ids=[cred._id], + client=client, + ) profile.create() cleanup.append(profile) assert_matches_server(profile) diff --git a/tests/test_api.py b/tests/test_api.py index 3ef9c6dc..acf41b59 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -7,8 +7,8 @@ from camayoc import config, exceptions, api from camayoc.qcs_models import ( - HostCredential, - NetworkProfile, + Credential, + Source, ) @@ -28,15 +28,17 @@ 'password': '********', 'ssh_keyfile': None, 'sudo_password': None, + 'cred_type': 'network', 'username': '6c71666b-df97-4d50-91bd-10003569e843' } -MOCK_PROFILE = { +MOCK_SOURCE = { 'credentials': [{'id': 34, 'name': '91311585-77b3-4352-a277-cf9507a04ffc' }], 'hosts': ['localhost'], 'id': 25, + 'source_type': 'network', 'name': 'e193081c-2423-4407-b9e2-05d20b6443dc', 'ssh_port': 22 } @@ -93,7 +95,7 @@ def test_invalid_hostname(self): api.Client() -class HostCredentialTestCase(unittest.TestCase): +class CredentialTestCase(unittest.TestCase): """Test :mod:camayoc.api.""" @classmethod @@ -105,7 +107,8 @@ def setUpClass(cls): def test_equivalent(self): """If a hostname is specified in the config file, we use it.""" with mock.patch.object(config, '_CONFIG', self.config): - h = HostCredential( + h = Credential( + cred_type='network', username=MOCK_CREDENTIAL['username'], name=MOCK_CREDENTIAL['name'] ) @@ -116,7 +119,7 @@ def test_equivalent(self): h.equivalent([]) -class NetworkProfileTestCase(unittest.TestCase): +class SourceTestCase(unittest.TestCase): """Test :mod:camayoc.api.""" @classmethod @@ -128,13 +131,14 @@ def setUpClass(cls): def test_equivalent(self): """If a hostname is specified in the config file, we use it.""" with mock.patch.object(config, '_CONFIG', self.config): - p = NetworkProfile( - name=MOCK_PROFILE['name'], - hosts=MOCK_PROFILE['hosts'], - credential_ids=[MOCK_PROFILE['credentials'][0]['id']] + p = Source( + source_type='network', + name=MOCK_SOURCE['name'], + hosts=MOCK_SOURCE['hosts'], + credential_ids=[MOCK_SOURCE['credentials'][0]['id']] ) - p._id = MOCK_PROFILE['id'] - self.assertTrue(p.equivalent(MOCK_PROFILE)) + p._id = MOCK_SOURCE['id'] + self.assertTrue(p.equivalent(MOCK_SOURCE)) self.assertTrue(p.equivalent(p)) with self.assertRaises(TypeError): p.equivalent([])