Address issues found by pep8, pylint and unit tests

flake8 is quite a bit more picky and discovered a lot of
issues.

Don't let missing configuration blow things up in order to be
able to run unit tests.

Hacky workaround for missing ipalib/ipapython in PyPy
This commit is contained in:
Rob Crittenden 2016-11-09 19:37:24 +00:00
parent 02aae0e7d4
commit ef2c9baa36
9 changed files with 107 additions and 70 deletions

View File

@ -1,13 +0,0 @@
# Copyright 2016 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

View File

@ -18,17 +18,18 @@
import paste.urlmap import paste.urlmap
import routes import routes
import webob.dec import webob.dec
from oslo_config import cfg from oslo_config import cfg
from oslo_context import context
from oslo_log import log from oslo_log import log
from oslo_middleware import request_id
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_service import wsgi from oslo_service import wsgi
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import strutils from oslo_utils import strutils
import six import six
import six.moves.urllib.parse as urlparse import six.moves.urllib.parse as urlparse
import webob.exc import webob.exc
from novajoin import exception from novajoin import exception
CONF = cfg.CONF CONF = cfg.CONF

View File

@ -1,4 +1,17 @@
#!/usr/bin/python #!/usr/bin/python
# Copyright 2016 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import getpass import getpass
import logging import logging
@ -7,17 +20,19 @@ import pwd
import socket import socket
import sys import sys
import tempfile import tempfile
from ipalib import api from ipalib import api
from ipalib import errors from ipalib import errors
from ipapython.ipautil import run, kinit_password, user_input from ipapython.ipautil import kinit_password
from ipapython.ipautil import run
from ipapython.ipautil import user_input
from novajoin.errors import ConfigurationError from novajoin.errors import ConfigurationError
logger = logging.getLogger() logger = logging.getLogger()
class NovajoinRole(object): class NovajoinRole(object):
""" """One-stop shopping for creating the IPA permissions, privilege and role.
One-stop shopping for creating the IPA permissions, privilege and role.
Assumes that ipalib is imported and initialized and an RPC context Assumes that ipalib is imported and initialized and an RPC context
already exists. already exists.
@ -30,9 +45,7 @@ class NovajoinRole(object):
self.ccache_name = None self.ccache_name = None
def _get_fqdn(self): def _get_fqdn(self):
""" """Try to determine the fully-qualfied domain name of this box"""
Try to determine the fully-qualfied domain name of this box
"""
fqdn = "" fqdn = ""
try: try:
fqdn = socket.getfqdn() fqdn = socket.getfqdn()
@ -67,8 +80,7 @@ class NovajoinRole(object):
os.environ['KRB5CCNAME'] = current_ccache os.environ['KRB5CCNAME'] = current_ccache
def _call_ipa(self, command, args, kw): def _call_ipa(self, command, args, kw):
""" """Call into the IPA API.
Call into the IPA API.
Duplicates are ignored to be idempotent. Other errors are Duplicates are ignored to be idempotent. Other errors are
ignored implitly because they are encapsulated in the result ignored implitly because they are encapsulated in the result

View File

@ -18,6 +18,7 @@ import copy
import inspect import inspect
import itertools import itertools
import random import random
import six
import sys import sys
import time import time
@ -26,9 +27,9 @@ from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import timeutils from oslo_utils import timeutils
from novajoin import exception from novajoin import exception
from novajoin import keystone_client from novajoin import keystone_client
import six
CONF = cfg.CONF CONF = cfg.CONF
@ -39,12 +40,20 @@ GLANCE_APIVERSION = 2
def get_api_servers(): def get_api_servers():
"""Return iterator of glance api_servers to cycle through the """Return iterator of glance api_servers.
Return iterator of glance api_servers to cycle through the
list, looping around to the beginning if necessary. list, looping around to the beginning if necessary.
""" """
api_servers = [] api_servers = []
ks = keystone_client.get_client() # Handle case where no credentials are configured
try:
ks = keystone_client.get_client()
except cfg.NoSuchOptError:
return []
catalog = keystone_client.get_service_catalog(ks) catalog = keystone_client.get_service_catalog(ks)
image_service = catalog.url_for(service_type='image') image_service = catalog.url_for(service_type='image')

View File

@ -15,9 +15,15 @@
import os import os
import uuid import uuid
from ipalib import api try:
from ipalib import errors from ipalib import api
from ipapython.ipautil import kinit_keytab from ipalib import errors
from ipapython.ipautil import kinit_keytab
ipalib_imported = True
except ImportError:
# ipalib/ipapython are not available in PyPy yet, don't make it
# a showstopper for the tests.
ipalib_imported = False
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
@ -31,7 +37,12 @@ LOG = logging.getLogger(__name__)
class IPANovaJoinBase(object): class IPANovaJoinBase(object):
def __init__(self): def __init__(self):
self.ntries = CONF.connect_retries try:
self.ntries = CONF.connect_retries
except cfg.NoSuchOptError:
self.ntries = 1
if not ipalib_imported:
return
self.ccache = "MEMORY:" + str(uuid.uuid4()) self.ccache = "MEMORY:" + str(uuid.uuid4())
os.environ['KRB5CCNAME'] = self.ccache os.environ['KRB5CCNAME'] = self.ccache
if self._ipa_client_configured() and not api.isdone('finalize'): if self._ipa_client_configured() and not api.isdone('finalize'):
@ -39,7 +50,7 @@ class IPANovaJoinBase(object):
api.finalize() api.finalize()
def __get_connection(self): def __get_connection(self):
"""Make a connection to IPA or raise an error""" """Make a connection to IPA or raise an error."""
tries = 0 tries = 0
while tries <= self.ntries: while tries <= self.ntries:
@ -48,7 +59,8 @@ class IPANovaJoinBase(object):
except (errors.CCacheError, errors.TicketExpired) as e: except (errors.CCacheError, errors.TicketExpired) as e:
LOG.debug("kinit again: %s", e) LOG.debug("kinit again: %s", e)
# pylint: disable=no-member # pylint: disable=no-member
kinit_keytab(str('nova/%s@%s' % (api.env.host, api.env.realm)), kinit_keytab(str('nova/%s@%s' %
(api.env.host, api.env.realm)),
CONF.keytab, CONF.keytab,
self.ccache) self.ccache)
tries += 1 tries += 1
@ -56,9 +68,12 @@ class IPANovaJoinBase(object):
return return
def _call_ipa(self, command, *args, **kw): def _call_ipa(self, command, *args, **kw):
"""Try twice to run the command. One execution may fail if we """Make an IPA call.
Try twice to run the command. One execution may fail if we
previously had a connection but the ticket expired. previously had a connection but the ticket expired.
""" """
if not api.Backend.rpcclient.isconnected(): if not api.Backend.rpcclient.isconnected():
self.__get_connection() self.__get_connection()
if 'version' not in kw: if 'version' not in kw:
@ -72,18 +87,21 @@ class IPANovaJoinBase(object):
api.Command[command](*args, **kw) api.Command[command](*args, **kw)
def _ipa_client_configured(self): def _ipa_client_configured(self):
"""Determine if the machine is an enrolled IPA client.
Return boolean indicating whether this machine is enrolled
in IPA. This is a rather weak detection method but better
than nothing.
""" """
Return boolean indicating whether this machine is enrolled
in IPA. This is a rather weak detection method but better
than nothing.
"""
return os.path.exists('/etc/ipa/default.conf') return os.path.exists('/etc/ipa/default.conf')
class IPAClient(IPANovaJoinBase): class IPAClient(IPANovaJoinBase):
def add_host(self, hostname, ipaotp, metadata=None, image_metadata=None): def add_host(self, hostname, ipaotp, metadata=None, image_metadata=None):
""" """Add a host to IPA.
If requested in the metadata, add a host to IPA. The assumption If requested in the metadata, add a host to IPA. The assumption
is that hostname is already fully-qualified. is that hostname is already fully-qualified.
@ -91,6 +109,7 @@ class IPAClient(IPANovaJoinBase):
multiple times, first we try to update the OTP in the host entry multiple times, first we try to update the OTP in the host entry
and if that fails due to NotFound the host is added. and if that fails due to NotFound the host is added.
""" """
LOG.debug('In IPABuildInstance') LOG.debug('In IPABuildInstance')
if not self._ipa_client_configured(): if not self._ipa_client_configured():
@ -127,6 +146,9 @@ class IPAClient(IPANovaJoinBase):
'userpassword': ipaotp.decode('UTF-8'), 'userpassword': ipaotp.decode('UTF-8'),
} }
if not ipalib_imported:
return True
try: try:
self._call_ipa('host_mod', *params, **modargs) self._call_ipa('host_mod', *params, **modargs)
except errors.NotFound: except errors.NotFound:
@ -143,9 +165,7 @@ class IPAClient(IPANovaJoinBase):
return True return True
def delete_host(self, hostname, metadata=None): def delete_host(self, hostname, metadata=None):
""" """Delete a host from IPA and remove all related DNS entries."""
Delete a host from IPA and remove all related DNS entries.
"""
LOG.debug('In IPADeleteInstance') LOG.debug('In IPADeleteInstance')
if not self._ipa_client_configured(): if not self._ipa_client_configured():
@ -155,8 +175,8 @@ class IPAClient(IPANovaJoinBase):
if metadata is None: if metadata is None:
metadata = {} metadata = {}
# TODO: lookup instance in nova to get metadata to see if # TODO(rcrit): lookup instance in nova to get metadata to see if
# the host was enrolled. For now assume yes. # the host was enrolled. For now assume yes.
params = [hostname] params = [hostname]
kw = { kw = {
@ -168,9 +188,7 @@ class IPAClient(IPANovaJoinBase):
pass pass
def add_ip(self, hostname, floating_ip): def add_ip(self, hostname, floating_ip):
""" """Add a floating IP to a given hostname."""
Add a floating IP to a given hostname.
"""
LOG.debug('In add_ip') LOG.debug('In add_ip')
if not self._ipa_client_configured(): if not self._ipa_client_configured():
@ -187,9 +205,7 @@ class IPAClient(IPANovaJoinBase):
pass pass
def remove_ip(self, hostname, floating_ip): def remove_ip(self, hostname, floating_ip):
""" """Remove a floating IP from a given hostname."""
Remove a floating IP from a given hostname.
"""
LOG.debug('In remove_ip') LOG.debug('In remove_ip')
if not self._ipa_client_configured(): if not self._ipa_client_configured():

View File

@ -12,16 +12,17 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import uuid
import logging import logging
import traceback import traceback
import uuid
import webob.exc import webob.exc
from oslo_serialization import jsonutils
from oslo_config import cfg from oslo_config import cfg
from novajoin.ipa import IPAClient
from novajoin import base from novajoin import base
from novajoin import exception from novajoin import exception
from novajoin.glance import get_default_image_service from novajoin.glance import get_default_image_service
from novajoin.ipa import IPAClient
CONF = cfg.CONF CONF = cfg.CONF
@ -108,15 +109,16 @@ class JoinController(Controller):
@response(200) @response(200)
def create(self, req, body=None): def create(self, req, body=None):
"""Generate the OTP, register it with IPA""" """Generate the OTP, register it with IPA
Options passed in but as yet-unused are project-id and user-data.
"""
if not body: if not body:
LOG.error('No body in create request') LOG.error('No body in create request')
raise base.Fault(webob.exc.HTTPBadRequest()) raise base.Fault(webob.exc.HTTPBadRequest())
project_id = body.get('project-id') # pylint: disable=unused-variable
instance_id = body.get('instance-id') instance_id = body.get('instance-id')
image_id = body.get('image-id') image_id = body.get('image-id')
user_data = body.get('user-data') # pylint: disable=unused-variable
hostname = body.get('hostname') hostname = body.get('hostname')
metadata = body.get('metadata', {}) metadata = body.get('metadata', {})
@ -168,12 +170,21 @@ class JoinController(Controller):
data['ipaotp'] = ipaotp data['ipaotp'] = ipaotp
if hostname: if hostname:
if CONF.project_subdomain: try:
# FIXME domain = CONF.domain
project = 'foo' except cfg.NoSuchOptError:
hostname = '%s.%s.%s' % (hostname, project, CONF.domain) domain = 'test'
try:
project_subdomain = CONF.project_subdomain
except cfg.NoSuchOptError:
hostname = '%s.%s' % (hostname, domain)
else: else:
hostname = '%s.%s' % (hostname, CONF.domain) if project_subdomain:
hostname = '%s.%s.%s' % (hostname,
project_subdomain, domain)
else:
hostname = '%s.%s' % (hostname, domain)
data['hostname'] = hostname data['hostname'] = hostname

View File

@ -24,8 +24,8 @@ from oslo_serialization import jsonutils
import webob.dec import webob.dec
import webob.exc import webob.exc
from novajoin import context
import novajoin.base import novajoin.base
from novajoin import context
CONF = cfg.CONF CONF = cfg.CONF

View File

@ -20,14 +20,15 @@
import sys import sys
import time import time
from novajoin.ipa import IPAClient
import oslo_messaging
from neutronclient.v2_0 import client as neutron_client from neutronclient.v2_0 import client as neutron_client
from novaclient import client as nova_client from novaclient import client as nova_client
from novajoin.keystone_client import get_session, register_keystoneauth_opts
from novajoin import config from novajoin import config
from oslo_serialization import jsonutils from novajoin.ipa import IPAClient
from novajoin.keystone_client import get_session
from novajoin.keystone_client import register_keystoneauth_opts
from oslo_log import log as logging from oslo_log import log as logging
import oslo_messaging
from oslo_serialization import jsonutils
CONF = config.CONF CONF = config.CONF

View File

@ -13,14 +13,14 @@
# under the License. # under the License.
import sys import sys
from novajoin import config
from novajoin import exception
from novajoin import keystone_client
from oslo_concurrency import processutils from oslo_concurrency import processutils
from oslo_log import log
from oslo_service import service from oslo_service import service
from oslo_service import wsgi from oslo_service import wsgi
from oslo_log import log
from novajoin import config
from novajoin import keystone_client
from novajoin import exception
CONF = config.CONF CONF = config.CONF