From 2d6c9489c840e92b31d7cbf50cf4fc6b88d88473 Mon Sep 17 00:00:00 2001 From: James Kasten Date: Wed, 3 Dec 2014 04:31:01 -0800 Subject: [PATCH] Add get_version_method (cannot currently make unittest) fix pylint errors. --- letsencrypt/client/apache_configurator.py | 116 +++++++++++++++++++--- letsencrypt/client/augeas_configurator.py | 9 +- letsencrypt/client/errors.py | 6 +- 3 files changed, 113 insertions(+), 18 deletions(-) diff --git a/letsencrypt/client/apache_configurator.py b/letsencrypt/client/apache_configurator.py index 11d29dac8..f297e1cf9 100644 --- a/letsencrypt/client/apache_configurator.py +++ b/letsencrypt/client/apache_configurator.py @@ -1,3 +1,4 @@ +"""Apache Configuration based off of Augeas Configurator.""" import hashlib import os import pkg_resources @@ -44,15 +45,31 @@ from letsencrypt.client import logger # transactional due to the use of register_file_creation() class VH(object): - """Represents an Apache Virtualhost.""" + """Represents an Apache Virtualhost. - def __init__(self, filename_path, vh_path, vh_addrs, is_ssl, is_enabled): - self.file = filename_path - self.path = vh_path - self.addrs = vh_addrs + :ivar str file: filename path of VH + + :ivar str path: Augeas path to virtual host + + :ivar list addrs: Virtual Host addresses (:class:`list` of :class:`str`) + + :ivar list names: Server names/aliases of vhost + (:class:`list` of :class:`str`) + + :ivar bool ssl: SSLEngine on in vhost + + :ivar bool enabled: Virtual host is enabled + + """ + + def __init__(self, filename, path, addrs, ssl, enabled): + """Initialize a VH.""" + self.file = filename + self.path = path + self.addrs = addrs self.names = [] - self.ssl = is_ssl - self.enabled = is_enabled + self.ssl = ssl + self.enabled = enabled def set_names(self, list_of_names): """Set names.""" @@ -62,6 +79,22 @@ class VH(object): """Add name to vhost.""" self.names.append(name) + def __str__(self): + return ("file: %s\n" + "vh_path: %s\n" + "addrs: %s\n" + "names: %s\n" + "ssl: %s\n" + "enabled: %s" % (self.file, self.path, self.addrs, + self.names, self.ssl, self.enabled)) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return (self.file == other.file and self.path == other.path and + set(self.addrs) == set(other.addrs) and + set(self.names) == set(other.naems) and + self.ssl == other.ssl and self.enabled == other.enabled) + class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Apache configurator. @@ -88,8 +121,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): so that other Configurators (like Nginx) can be developed and interoperate with the client. + :ivar str server_root: Path to Apache root directory + + :ivar float version: version of Apache + + :ivar str user_config_file: Path to the user's configuration file + + :ivar list vhosts: All vhosts found in the configuration + (:class:`list` of :class:`VH`) + + :ivar dict assoc: Mapping between domains and vhosts + """ - def __init__(self, server_root=CONFIG.SERVER_ROOT): + def __init__(self, server_root=CONFIG.SERVER_ROOT, version=None): + """Initialize an Apache Configurator.""" super(ApacheConfigurator, self).__init__() self.server_root = server_root @@ -99,6 +144,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # because this will change the underlying configuration and potential # vhosts self.recovery_routine() + + # Set Version + if not version: + self.version = self.get_version() + else: + self.version = version + # Check for errors in parsing files with Augeas self.check_parsing_errors("httpd.aug") # This problem has been fixed in Augeas 1.0 @@ -631,7 +683,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # TODO: Test if Apache allows ../ or ~/ for Includes # Attempts to add a transform to the file if one does not already exist - self.parse_file(arg) + self._parse_file(arg) # Argument represents an fnmatch regular expression, convert it # Split up the path and convert each into an Augeas accepted regex @@ -1087,7 +1139,7 @@ LogLevel warn \n\ return True return False - def fnmatch_to_re(self, clean_fn_match): + def fnmatch_to_re(self, clean_fn_match): # pylint: disable=no-self-use """Method converts Apache's basic fnmatch to regular expression. :param str clean_fn_match: Apache style filename match, similar to globs @@ -1110,7 +1162,7 @@ LogLevel warn \n\ regex = regex + letter return regex - def parse_file(self, file_path): + def _parse_file(self, file_path): """Parse file with Augeas Checks to see if file_path is parsed by Augeas @@ -1170,7 +1222,7 @@ LogLevel warn \n\ self.aug.load() - def restart(self, quiet=False): # pylint: disable=no-self-use + def restart(self, quiet=False): # pylint: disable=no-self-use """Restarts apache server. :returns: Success @@ -1218,6 +1270,37 @@ LogLevel warn \n\ return True + def get_version(self): # pylint: disable=no-self-use + """Return version of Apache Server. + + Version is returned as float. (ie. 2.4.7 = 2.47) + + :returns: version + :rtype: float + + """ + try: + proc = subprocess.Popen( + ['sudo', '/usr/sbin/apache2ctl', '-v'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + text = proc.communicate()[0] + except (OSError, ValueError): + raise errors.LetsEncryptConfiguratorError( + "Unable to run /usr/sbin/apache2ctl -v") + + regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) + matches = regex.findall(text) + + if len(matches) != 1: + raise errors.LetsEncryptConfiguratorError( + "Unable to find Apache version") + + num_decimal = matches[0].count(".") + + # Format return value such as 2.47 rather than 2.4.7 + return float("".join(matches[0].rsplit(".", num_decimal-1))) + ########################################################################### # Challenges Section ########################################################################### @@ -1436,7 +1519,12 @@ def check_ssl_loaded(): def apache_restart(quiet=False): - # TODO: This should be written to use the process returncode + """Restarts the Apache Server. + + .. todo:: Try to use reload instead. (This caused timing problems before) + .. todo:: This should be written to use the process return code. + + """ try: proc = subprocess.Popen(['/etc/init.d/apache2', 'restart'], stdout=subprocess.PIPE, @@ -1614,7 +1702,7 @@ def main(): # print config.get_all_names() # test_file = "/home/james/Desktop/ports_test.conf" - # config.parse_file(test_file) + # config._parse_file(test_file) # config.aug.insert("/files"+test_file+"/IfModule[1]/arg","directive",False) # config.aug.set("/files"+test_file+"/IfModule[1]/directive[1]", "Listen") diff --git a/letsencrypt/client/augeas_configurator.py b/letsencrypt/client/augeas_configurator.py index 702f863ea..5a81ed5f7 100644 --- a/letsencrypt/client/augeas_configurator.py +++ b/letsencrypt/client/augeas_configurator.py @@ -1,3 +1,4 @@ +"""Class of Augeas Configurators.""" import os import sys import shutil @@ -291,7 +292,7 @@ class AugeasConfigurator(configurator.Configurator): return 0 - def check_tempfile_saves(self, save_files): + def check_tempfile_saves(self, save_files): # pylint: disable=no-self-use """Verify save isn't overwriting any temporary files. :param set save_files: Set of files about to be saved. @@ -311,6 +312,7 @@ class AugeasConfigurator(configurator.Configurator): return True, "" + # pylint: disable=no-self-use def register_file_creation(self, temporary, *files): """Register the creation of all files during letsencrypt execution. @@ -361,6 +363,7 @@ class AugeasConfigurator(configurator.Configurator): # Need to reload configuration after these changes take effect self.aug.load() + # pylint: disable=no-self-use def _remove_contained_files(self, file_list): """Erase all files contained within file_list. @@ -394,8 +397,8 @@ class AugeasConfigurator(configurator.Configurator): return True - - def _finalize_checkpoint(cp_dir, title): # pylint: disable=no-self-use + # pylint: disable=no-self-use + def _finalize_checkpoint(self, cp_dir, title): """Move IN_PROGRESS checkpoint to timestamped checkpoint. Adds title to cp_dir CHANGES_SINCE diff --git a/letsencrypt/client/errors.py b/letsencrypt/client/errors.py index 4f93860be..dddfc5e4e 100644 --- a/letsencrypt/client/errors.py +++ b/letsencrypt/client/errors.py @@ -5,5 +5,9 @@ class LetsEncryptClientError(Exception): """Generic Let's Encrypt client error.""" -class LetsEncryptDvsniError(LetsEncryptClientError): +class LetsEncryptConfiguratorError(LetsEncryptClientError): + """Let's Encrypt configurator error.""" + + +class LetsEncryptDvsniError(LetsEncryptConfiguratorError): """Let's Encrypt DVSNI error."""