Skip to content

Commit

Permalink
Refactor to remove duplicated code, improve names
Browse files Browse the repository at this point in the history
Adds a new base class for shared attributes and methods of
HostCredential and NetworkProfile objects.

Updates names of the QCSClient methods to be more human readable.

Refactors the cleanup fixture to only cleanup after the test with which
it is associated.
  • Loading branch information
kdelee committed Oct 11, 2017
1 parent b6ded3f commit b9cbbbb
Show file tree
Hide file tree
Showing 16 changed files with 191 additions and 166 deletions.
74 changes: 40 additions & 34 deletions camayoc/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@

from camayoc import config
from camayoc import exceptions
from camayoc.constants import (
QCS_API_VERSION,
QCS_CREDENTIALS_PATH,
QCS_PROFILES_PATH,
)


def echo_handler(server_config, response): # pylint:disable=unused-argument
Expand Down Expand Up @@ -67,7 +72,7 @@ class Client(object):
>>>
>>> # now if I want to do something else,
>>> # I can change the base url
>>> client.url = 'www.whatever.com'
>>> client.url = 'https://www.whatever.com'
.. _Requests: http://docs.python-requests.org/en/master/
"""
Expand All @@ -82,20 +87,20 @@ def __init__(self, response_handler=None, url=None):
camayoc config file:
qcs:
hostname: 'hostname_or_ip_with_port'
hostname: 'http://hostname_or_ip_with_port'
"""
self._cfg = config.get_config()
qcs_settings = self._cfg.get('qcs')
self.url = None
if url:
self.url = urljoin(url, 'api/v1/')
self.url = self.url = urljoin(url, QCS_API_VERSION) if url else None

if qcs_settings and not url:
if not qcs_settings.get('hostname'):
raise exceptions.QCSBaseUrlNotFound(
'\n\'qcs\' section specified in camayoc config file, but'
' no \'hostname\' key found.'
)
self.url = urljoin(qcs_settings.get('hostname'), 'api/v1/')
self.url = urljoin(qcs_settings.get('hostname'), QCS_API_VERSION)

if not self.url:
raise exceptions.QCSBaseUrlNotFound(
'\nNo base url was specified to the client'
Expand All @@ -106,6 +111,7 @@ def __init__(self, response_handler=None, url=None):
'A hostname was provided, but we could not use it.'
'\nValid hostnames start with http:// or https://'
)

if response_handler is None:
self.response_handler = code_handler
else:
Expand Down Expand Up @@ -164,7 +170,7 @@ class QCSClient(Client):
Example::
>>> from camayoc import api
>>> client = api.QCSClient()
>>> client.read_host_creds()
>>> client.read_credentials()
"""

def __init__(self, *args, **kwargs):
Expand All @@ -173,52 +179,52 @@ def __init__(self, *args, **kwargs):
The call to super will set base url from the camayoc config file,
as well as set the response_handler that the client will use.
"""
self.host_cred_path = 'credentials/hosts/'
self.net_prof_path = 'profiles/networks/'
self.credential_path = QCS_CREDENTIALS_PATH
self.profile_path = QCS_PROFILES_PATH
super(QCSClient, self).__init__(*args, **kwargs)

def create_host_cred(self, payload):
def create_credential(self, payload):
"""Send POST to QCS to create new host credential."""
return self.post(self.host_cred_path, payload)
return self.post(self.credential_path, payload)

def read_host_creds(self, host_cred_id=None):
def read_credentials(self, credential_id=None):
"""Send GET request to read host credentials.
If no host_cred_id is specified, get all host credentials,
If no credential_id is specified, get all host credentials,
otherwise just get the one specified.
"""
path = self.host_cred_path
if host_cred_id:
path = urljoin(path, '{}/'.format(host_cred_id))
path = self.credential_path
if credential_id:
path = urljoin(path, '{}/'.format(credential_id))
return self.get(path)

def update_host_cred(self, host_cred_id, payload):
"""Send PUT request to update given host_cred_id with new data."""
path = urljoin(self.host_cred_path, '{}/'.format(host_cred_id))
def update_credential(self, credential_id, payload):
"""Send PUT request to update given credential_id with new data."""
path = urljoin(self.credential_path, '{}/'.format(credential_id))
return self.put(path, payload)

def delete_host_cred(self, host_cred_id):
"""Send DELETE request for host_cred_id to QCS."""
path = urljoin(self.host_cred_path, '{}/'.format(host_cred_id))
def delete_credential(self, credential_id):
"""Send DELETE request for credential_id to QCS."""
path = urljoin(self.credential_path, '{}/'.format(credential_id))
return self.delete(path)

def create_net_prof(self, payload):
def create_profile(self, payload):
"""Send POST to QCS to create new network profile."""
return self.post(self.net_prof_path, payload)
return self.post(self.profile_path, payload)

def read_net_profs(self, net_prof_id=None):
def read_profiles(self, profile_id=None):
"""Send GET request to read all network profiles."""
path = self.net_prof_path
if net_prof_id:
path = urljoin(path, '{}/'.format(net_prof_id))
path = self.profile_path
if profile_id:
path = urljoin(path, '{}/'.format(profile_id))
return self.get(path)

def update_net_prof(self, net_prof_id, payload):
"""Send PUT request to update given net_prof_id with new data."""
path = urljoin(self.host_cred_path, '{}/'.format(net_prof_id))
def update_profile(self, profile_id, payload):
"""Send PUT request to update given profile_id with new data."""
path = urljoin(self.credential_path, '{}/'.format(profile_id))
return self.put(path, payload)

def delete_net_prof(self, net_prof_id):
"""Send DELETE request for net_prof_id to QCS."""
path = urljoin(self.net_prof_path, '{}/'.format(net_prof_id))
def delete_profile(self, profile_id):
"""Send DELETE request for profile_id to QCS."""
path = urljoin(self.profile_path, '{}/'.format(profile_id))
return self.delete(path)
9 changes: 9 additions & 0 deletions camayoc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,12 @@

VCENTER_HOST = 0
"""The index of the host in the cluster in the VCenter MOB"""

QCS_API_VERSION = 'api/v1/'
"""The root path to access the QCS server API."""

QCS_CREDENTIALS_PATH = 'credentials/hosts/'
"""The path to the credentials endpoint for CRUD tasks."""

QCS_PROFILES_PATH = 'profiles/networks/'
"""The path to the profiles endpoint for CRUD tasks."""
2 changes: 1 addition & 1 deletion camayoc/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ class QCSBaseUrlNotFound(Exception):
config file in the following manner:
qcs:
hostname: 'hostname_or_ip_with_port'
hostname: 'http://hostname_or_ip_with_port'
"""
114 changes: 43 additions & 71 deletions camayoc/qcs_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,41 @@
from camayoc.constants import MASKED_PASSWORD_OUTPUT


class HostCredential(object):
class QCSObject(object):
"""A base class for other QCS models."""

def __init__(
self,
name=None,
_id=None):
"""Provide shared methods for QCS model objects."""
self.name = str(uuid.uuid4()) if not name else name
self._id = _id

def fields(self):
"""Return a dictionary with all fields."""
fields = self.payload()
fields['id'] = self._id
return fields

def payload(self):
"""Return a dictionary for POST or PUT requests."""
return {k: v for k, v in self.__dict__.items() if k != '_id'}

def to_str(self):
"""Return the string representation of the model."""
return pformat(self.__dict__)

def __repr__(self):
"""For `print` and `pprint`."""
return self.to_str()

def __ne__(self, other):
"""Return true if both objects are not equal."""
return not self == other


class HostCredential(QCSObject):
"""A class to aid in CRUD test cases for host credentials."""

def __init__(
Expand All @@ -30,43 +64,16 @@ def __init__(
Example::
>>> from camayoc import api
>>> from camayoc.qcs_models import HostCredential
>>> client = api.QCS_Client()
>>> host = HostCredential()
>>> host.password = 'foo'
>>> create_response = client.create_host_cred(host.payload())
>>> client = api.QCSClient()
>>> host = HostCredential(password='foo')
>>> create_response = client.create_credential(host.payload())
>>> host._id = create_response.json()['id']
"""
self.name = str(uuid.uuid4()) if not name else name
super(HostCredential, self).__init__(name=name, _id=_id)
self.username = str(uuid.uuid4()) if not username else username
self.password = password
self.ssh_keyfile = ssh_keyfile
self.sudo_password = sudo_password
self._id = _id

def fields(self):
"""Return a dictionary with all fields."""
fields = self.payload()
fields['id'] = self._id
return fields

def payload(self):
"""Return a dictionary for POST or PUT requests.
Returns proper data for creating host credential with POST to
/api/vi/credentials/hosts/ or updating host credential with PUT to
/api/v1/credentials/hosts/{_id}
"""
packet = {k: v for k, v in self.__dict__.items() if k != '_id'}
return packet

def to_str(self):
"""Return the string representation of the model."""
return pformat(self.__dict__)

def __repr__(self):
"""For `print` and `pprint`."""
return self.to_str()

def __eq__(self, other):
"""Return true if both objects are equal."""
Expand All @@ -84,12 +91,8 @@ def __eq__(self, other):
diffs += 1
return diffs == 0

def __ne__(self, other):
"""Return true if both objects are not equal."""
return not self == other


class NetworkProfile(object):
class NetworkProfile(QCSObject):
"""A class to aid in CRUD test cases for network profiles."""

def __init__(
Expand All @@ -109,47 +112,20 @@ def __init__(
Example::
>>> from camayoc import api
>>> from camayoc.qcs_models import NetworkProfile
>>> client = api.QCS_Client()
>>> client = api.QCSClient()
>>> hostcred = HostCredential()
>>> hostcred.password = 'foo'
>>> hostcred = HostCredential(password='foo')
>>> create_response = client.create_host_cred(hostcred.payload())
>>> hostcred._id = create_response.json()['id']
>>> netprof = NetworkProfile(hosts=['0.0.0.0'],
credential_ids=['host._id'])
>>> client.create_net_prof(netprof.payload())
"""
self.name = str(uuid.uuid4()) if not name else name
super(NetworkProfile, self).__init__(name=name, _id=_id)
self.hosts = hosts
self.ssh_port = 22 if not name else name
self.credentials = credential_ids
self._id = _id

def fields(self):
"""Return a dictionary with all fields."""
fields = self.payload()
fields['id'] = self._id
return fields

def payload(self):
"""Return a dictionary for POST or PUT requests.
Returns proper data for creating host credential with POST to
/api/vi/profiles/networks/ or updating host credential with PUT to
/api/v1/profiles/networks/{_id}
"""
packet = {k: v for k, v in self.__dict__.items() if k != '_id'}
return packet

def to_str(self):
"""Return the string representation of the model."""
return pformat(self.__dict__)

def __repr__(self):
"""For `print` and `pprint`."""
return self.to_str()

def __eq__(self, other):
"""Return true if both objects are equal."""
Expand All @@ -169,7 +145,3 @@ def __eq__(self, other):
if not other.get(key) == value:
diffs += 1
return diffs == 0

def __ne__(self, other):
"""Return true if both objects are not equal."""
return not self == other
10 changes: 5 additions & 5 deletions camayoc/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# coding=utf-8
"""Tests for RHO.
"""Tests for quipucords projects.
This package and its sub-packages contain functional tests for RHO. These tests
should be run against RHO installations. These tests run RHO remotely (trhough
a SSH connection) or locally. These tests are entirely different from the unit
tests in :mod:`tests`.
This package and its sub-packages contain functional tests for RHO and QCS.
These tests should be run against respective RHO or QCS installations. These
tests run remotely (trhough a SSH connection) or locally. These tests are
entirely different from the unit tests in :mod:`tests`.
"""
2 changes: 1 addition & 1 deletion camayoc/tests/qcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
your camayoc config file with the following information:
'qcs':
hostname: 'hostname_or_ip_with_port'
hostname: 'http://hostname_or_ip_with_port'
Be sure to specify the port if you are running on a non-standard one.
"""
32 changes: 20 additions & 12 deletions camayoc/tests/qcs/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@


@pytest.fixture
def test_cleanup(request):
def cleanup_credentials():
"""Fixture that cleans up any created host credentials."""
def cleanup():
client = api.QCSClient()
read_response = client.read_host_creds()
for cred in read_response.json():
if cred.get('id'):
client.delete_host_cred(cred.get('id'))
read_response = client.read_net_profs()
for prof in read_response.json():
if prof.get('id'):
client.delete_net_prof(prof.get('id'))
request.addfinalizer(cleanup)
credential_ids = []

yield credential_ids

client = api.QCSClient()
for _id in credential_ids:
client.delete_credential(_id)


@pytest.fixture
def cleanup_profiles():
"""Fixture that cleans up any created network profiles."""
profile_ids = []

yield profile_ids

client = api.QCSClient()
for _id in profile_ids:
client.delete_profile(_id)
Loading

0 comments on commit b9cbbbb

Please sign in to comment.