Hi Krzysztof,

first thank you for bringing up the topic of wider use of FreeIPA on 
different platforms.

On Sun, 26 Feb 2012, Krzysztof Klimonda wrote:
> Hey,
> 
>   tjaalton has pointed you as a most likely suspect for contacting
> regarding adding some support for Debian to the FreeIPA. We're both
> working on getting server and client bits packaged for Debian/Ubuntu and
> I took a look into FreeIPA to see how much has to be changed to add
> support for those distributions so both server and client work.
All you describe makes sense. I'd suggest to split separate issues 
into different tickets and file them at 
https://fedorahosted.org/freeipa/newticket. 

You can use 'Blocking' field to make sure dependencies are ordered 
properly.

Contributing the implementation is definitely welcomed.

Let me summarize your proposal based on the prototype patches below:
1. Turn ipapython.platform.base into a full-featured module that 
provides reference implementations of various services. These would 
cover most of existing codepaths from ipaserver.install.*instance.py 
and detection logic from ipa-server-install/ipa-client-install

2. Allow to re-implement these services in platform-specific code, 
ipapython.platform.<system>, trying to re-use 
ipapython.platform.base.* as much as possible.

3. Make ipaserver.install.*instance.py be largerly high-level wrappers  
for ipapython.platform.* that manage ordering of configuration from 
the FreeIPA perspective.

Besides the things you point out, one would need to solve major 
issue of a certificate management. Currently openssl/nss differences 
are affecting also common certificate stores on different platforms. This 
is bigger problem than platform-specific configuration of existing 
services.

> There are at least three things that have to be done:
>   - removal of acutil -- There has been some discussion about it on
> freeipa-devel, and there is already a ticket [1] opened about it but I'm
> not sure what's the current status.
We'd like to move to async DNS resolver as well. Probably by FreeIPA 
3.1 or so, depending on the amount of work needed to integrate it as 
there are many code paths that imply the use of acutil-produced 
lists/arrays.

>   - debian-specific platform module -- We need Debian-specific
> implementations of all the functions and classes from the base module,
> but it's not a big deal - a lot of this stuff has already been done,
> I've also started digging through Debian documentation on how to
> implement AuthConfig class.
Yes, AuthConfig is currently one of biggest missing pieces, along with 
commong certificate store.

>   - all the "instance" modules (like ntpinstance.py or httpinstance.py)
> are not written with other distributions in mind. It's not just the
> location of the files, Debian does a lot of stuff (like apache
> configuration) differently so the best course of action seems to be
> moving this code into platform-specific modules, the same as with
> services and authorization stuff.
Yes, looks fine.

 
>  I've spent some time at lest prototyping the code based on 2.1.4 but
> before I actually start writing anything solid I'd like your opinion on
> my current approach - maybe you have some plans related to that, or
> already some working code?
As I pointed above, I'm OK with the approach.

 
>  I'm attaching [2] two patches that basically start moving stuff around
> inside platform package. They are not in any shape to post them on
> freeipa-devel - as I've mentioned I'd like to hear your input before
> writing code.
Don't be shy :) I'm posting my answer to freeipa-devel@ as I think it 
is already in a shape for discussing it with wider audience.

> 
> Cheers,
>  KK
> 
> [1] https://fedorahosted.org/freeipa/ticket/1837
> [2] I've tried pushing repository to github but it fails and complains
> about commit 0b36ce6dcbfc8d7e6cda632e06a09c369428a2db having a wrong
> date (which it has) and I'm not sure how to actually work around it.

> From aaa0ae39a88c9465d8d73590a4d2ba3d140ab69a Mon Sep 17 00:00:00 2001
> From: Krzysztof Klimonda <kklimo...@syntaxhighlighted.com>
> Date: Sat, 25 Feb 2012 20:57:28 +0100
> Subject: [PATCH 1/2] convert the base platform module into package
> 
> This package will provide base classes for both services and instances
> that have to be implemented for each platform to provide needed interface
> between FreeIPA and the system it's being run on.
> ---
>  ipapython/platform/base.py                    |  150 -----------
>  ipapython/platform/base/__init__.py           |  150 +++++++++++
>  ipapython/platform/base/instances/__init__.py |    1 +
>  ipapython/platform/base/instances/ntp.py      |  181 ++++++++++++++
>  ipapython/platform/base/instances/service.py  |  330 
> +++++++++++++++++++++++++
>  ipaserver/install/service.py                  |  330 
> -------------------------
>  6 files changed, 662 insertions(+), 480 deletions(-)
>  delete mode 100644 ipapython/platform/base.py
>  create mode 100644 ipapython/platform/base/__init__.py
>  create mode 100644 ipapython/platform/base/instances/__init__.py
>  create mode 100644 ipapython/platform/base/instances/ntp.py
>  create mode 100644 ipapython/platform/base/instances/service.py
>  delete mode 100644 ipaserver/install/service.py
> 
> diff --git a/ipapython/platform/base.py b/ipapython/platform/base.py
> deleted file mode 100644
> index 99189a1..0000000
> --- a/ipapython/platform/base.py
> +++ /dev/null
> @@ -1,150 +0,0 @@
> -# Authors: Alexander Bokovoy <aboko...@redhat.com>
> -#
> -# Copyright (C) 2011  Red Hat
> -# see file 'COPYING' for use and warranty information
> -#
> -# This program is free software; you can redistribute it and/or modify
> -# it under the terms of the GNU General Public License as published by
> -# the Free Software Foundation, either version 3 of the License, or
> -# (at your option) any later version.
> -#
> -# This program is distributed in the hope that it will be useful,
> -# but WITHOUT ANY WARRANTY; without even the implied warranty of
> -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> -# GNU General Public License for more details.
> -#
> -# You should have received a copy of the GNU General Public License
> -# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> -
> -from ipalib.plugable import MagicDict
> -
> -# Canonical names of services as IPA wants to see them. As we need to have 
> *some* naming,
> -# set them as in Red Hat distributions. Actual implementation should make 
> them available
> -# through knownservices.<name> and take care of remapping internally, if 
> needed
> -wellknownservices = ['certmonger', 'dirsrv', 'httpd', 'ipa', 'krb5kdc', 
> 'messagebus',
> -                     'nslcd', 'nscd', 'ntpd', 'portmap', 'rpcbind', 'kadmin']
> -
> -class AuthConfig(object):
> -    """
> -    AuthConfig class implements system-independent interface to configure
> -    system authentication resources. In Red Hat systems this is done with
> -    authconfig(8) utility.
> -
> -    AuthConfig class is nothing more than a tool to gather configuration 
> options
> -    and execute their processing. These options then converted by an actual 
> implementation
> -    to series of a system calls to appropriate utilities performing real 
> configuration.
> -
> -    IPA *expects* names of AuthConfig's options to follow authconfig(8) 
> naming scheme!
> -
> -    Actual implementation should be done in ipapython/platform/<platform>.py 
> by inheriting from
> -    platform.AuthConfig and redefining __build_args() and execute() methods.
> -
> -    from ipapython.platform import platform
> -    class PlatformAuthConfig(platform.AuthConfig):
> -        def __build_args():
> -        ...
> -
> -        def execute():
> -        ...
> -
> -    authconfig = PlatformAuthConfig
> -    ....
> -
> -    See ipapython/platform/redhat.py for a sample implementation that uses 
> authconfig(8) as its backend.
> -
> -    From IPA code perspective, the authentication configuration should be 
> done with use of ipapython.services.authconfig:
> -
> -    from ipapython import services as ipaservices
> -    auth_config = ipaservices.authconfig()
> -    auth_config.disable("ldap").\
> -                disable("krb5").\
> -                disable("sssd").\
> -                disable("sssdauth").\
> -                disable("mkhomedir").\
> -                add_option("update").\
> -                enable("nis").\
> -                add_parameter("nisdomain","foobar")
> -    auth_config.execute()
> -
> -    If you need to re-use existing AuthConfig instance for multiple runs, 
> make sure to
> -    call 'AuthConfig.reset()' between the runs.
> -    """
> -
> -    def __init__(self):
> -        self.parameters = {}
> -
> -    def enable(self, option):
> -        self.parameters[option] = True
> -        return self
> -
> -    def disable(self, option):
> -        self.parameters[option] = False
> -        return self
> -
> -    def add_option(self, option):
> -        self.parameters[option] = None
> -        return self
> -
> -    def add_parameter(self, option, value):
> -        self.parameters[option] = [value]
> -        return self
> -
> -    def __build_args(self):
> -        # do nothing
> -        return None
> -
> -    def execute(self):
> -        # do nothing
> -        return None
> -
> -    def reset(self):
> -        self.parameters = {}
> -        return self
> -
> -class PlatformService(object):
> -    """
> -    PlatformService abstracts out external process running on the system 
> which is possible
> -    to administer (start, stop, check status, etc).
> -
> -    """
> -
> -    def __init__(self, service_name):
> -        self.service_name = service_name
> -
> -    def start(self, instance_name="", capture_output=True):
> -        return
> -
> -    def stop(self, instance_name="", capture_output=True):
> -        return
> -
> -    def restart(self, instance_name="", capture_output=True):
> -        return
> -
> -    def is_running(self, instance_name=""):
> -        return False
> -
> -    def is_installed(self):
> -        return False
> -
> -    def is_enabled(self, instance_name=""):
> -        return False
> -
> -    def enable(self, instance_name=""):
> -        return
> -
> -    def disable(self, instance_name=""):
> -        return
> -
> -    def install(self, instance_name=""):
> -        return
> -
> -    def remove(self, instance_name=""):
> -        return
> -
> -class KnownServices(MagicDict):
> -    """
> -    KnownServices is an abstract class factory that should give out 
> instances of well-known
> -    platform services. Actual implementation must create these instances as 
> its own attributes
> -    on first access (or instance creation) and cache them.
> -    """
> -
> diff --git a/ipapython/platform/base/__init__.py 
> b/ipapython/platform/base/__init__.py
> new file mode 100644
> index 0000000..99189a1
> --- /dev/null
> +++ b/ipapython/platform/base/__init__.py
> @@ -0,0 +1,150 @@
> +# Authors: Alexander Bokovoy <aboko...@redhat.com>
> +#
> +# Copyright (C) 2011  Red Hat
> +# see file 'COPYING' for use and warranty information
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +from ipalib.plugable import MagicDict
> +
> +# Canonical names of services as IPA wants to see them. As we need to have 
> *some* naming,
> +# set them as in Red Hat distributions. Actual implementation should make 
> them available
> +# through knownservices.<name> and take care of remapping internally, if 
> needed
> +wellknownservices = ['certmonger', 'dirsrv', 'httpd', 'ipa', 'krb5kdc', 
> 'messagebus',
> +                     'nslcd', 'nscd', 'ntpd', 'portmap', 'rpcbind', 'kadmin']
> +
> +class AuthConfig(object):
> +    """
> +    AuthConfig class implements system-independent interface to configure
> +    system authentication resources. In Red Hat systems this is done with
> +    authconfig(8) utility.
> +
> +    AuthConfig class is nothing more than a tool to gather configuration 
> options
> +    and execute their processing. These options then converted by an actual 
> implementation
> +    to series of a system calls to appropriate utilities performing real 
> configuration.
> +
> +    IPA *expects* names of AuthConfig's options to follow authconfig(8) 
> naming scheme!
> +
> +    Actual implementation should be done in ipapython/platform/<platform>.py 
> by inheriting from
> +    platform.AuthConfig and redefining __build_args() and execute() methods.
> +
> +    from ipapython.platform import platform
> +    class PlatformAuthConfig(platform.AuthConfig):
> +        def __build_args():
> +        ...
> +
> +        def execute():
> +        ...
> +
> +    authconfig = PlatformAuthConfig
> +    ....
> +
> +    See ipapython/platform/redhat.py for a sample implementation that uses 
> authconfig(8) as its backend.
> +
> +    From IPA code perspective, the authentication configuration should be 
> done with use of ipapython.services.authconfig:
> +
> +    from ipapython import services as ipaservices
> +    auth_config = ipaservices.authconfig()
> +    auth_config.disable("ldap").\
> +                disable("krb5").\
> +                disable("sssd").\
> +                disable("sssdauth").\
> +                disable("mkhomedir").\
> +                add_option("update").\
> +                enable("nis").\
> +                add_parameter("nisdomain","foobar")
> +    auth_config.execute()
> +
> +    If you need to re-use existing AuthConfig instance for multiple runs, 
> make sure to
> +    call 'AuthConfig.reset()' between the runs.
> +    """
> +
> +    def __init__(self):
> +        self.parameters = {}
> +
> +    def enable(self, option):
> +        self.parameters[option] = True
> +        return self
> +
> +    def disable(self, option):
> +        self.parameters[option] = False
> +        return self
> +
> +    def add_option(self, option):
> +        self.parameters[option] = None
> +        return self
> +
> +    def add_parameter(self, option, value):
> +        self.parameters[option] = [value]
> +        return self
> +
> +    def __build_args(self):
> +        # do nothing
> +        return None
> +
> +    def execute(self):
> +        # do nothing
> +        return None
> +
> +    def reset(self):
> +        self.parameters = {}
> +        return self
> +
> +class PlatformService(object):
> +    """
> +    PlatformService abstracts out external process running on the system 
> which is possible
> +    to administer (start, stop, check status, etc).
> +
> +    """
> +
> +    def __init__(self, service_name):
> +        self.service_name = service_name
> +
> +    def start(self, instance_name="", capture_output=True):
> +        return
> +
> +    def stop(self, instance_name="", capture_output=True):
> +        return
> +
> +    def restart(self, instance_name="", capture_output=True):
> +        return
> +
> +    def is_running(self, instance_name=""):
> +        return False
> +
> +    def is_installed(self):
> +        return False
> +
> +    def is_enabled(self, instance_name=""):
> +        return False
> +
> +    def enable(self, instance_name=""):
> +        return
> +
> +    def disable(self, instance_name=""):
> +        return
> +
> +    def install(self, instance_name=""):
> +        return
> +
> +    def remove(self, instance_name=""):
> +        return
> +
> +class KnownServices(MagicDict):
> +    """
> +    KnownServices is an abstract class factory that should give out 
> instances of well-known
> +    platform services. Actual implementation must create these instances as 
> its own attributes
> +    on first access (or instance creation) and cache them.
> +    """
> +
> diff --git a/ipapython/platform/base/instances/__init__.py 
> b/ipapython/platform/base/instances/__init__.py
> new file mode 100644
> index 0000000..40f925b
> --- /dev/null
> +++ b/ipapython/platform/base/instances/__init__.py
> @@ -0,0 +1 @@
> +from .ntp import NTPInstance
> diff --git a/ipapython/platform/base/instances/ntp.py 
> b/ipapython/platform/base/instances/ntp.py
> new file mode 100644
> index 0000000..00217c3
> --- /dev/null
> +++ b/ipapython/platform/base/instances/ntp.py
> @@ -0,0 +1,181 @@
> +# Authors: Karl MacMillan <kmacmil...@redhat.com>
> +# Authors: Simo Sorce <sso...@redhat.com>
> +#
> +# Copyright (C) 2007-2010  Red Hat
> +# see file 'COPYING' for use and warranty information
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +#
> +
> +import logging
> +
> +import service
> +from ipapython import sysrestore
> +from ipapython import ipautil
> +from ipapython import services as ipaservices
> +
> +class NTPInstance(service.Service):
> +    def __init__(self, fstore=None):
> +        service.Service.__init__(self, "ntpd")
> +
> +        if fstore:
> +            self.fstore = fstore
> +        else:
> +            self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
> +
> +    def __write_config(self):
> +
> +        self.fstore.backup_file("/etc/ntp.conf")
> +        self.fstore.backup_file("/etc/sysconfig/ntpd")
> +
> +        # We use the OS variable to point it towards either the rhel
> +        # or fedora pools. Other distros should be added in the future
> +        # or we can get our own pool.
> +        os = ""
> +        if ipautil.file_exists("/etc/fedora-release"):
> +            os = "fedora"
> +        elif ipautil.file_exists("/etc/redhat-release"):
> +            os = "rhel"
> +
> +        srv_vals = []
> +        srv_vals.append("0.%s.pool.ntp.org" % os)
> +        srv_vals.append("1.%s.pool.ntp.org" % os)
> +        srv_vals.append("2.%s.pool.ntp.org" % os)
> +        srv_vals.append("127.127.1.0")
> +        fudge = ["fudge", "127.127.1.0", "stratum", "10"]
> +
> +        #read in memory, change it, then overwrite file
> +        file_changed = False
> +        fudge_present = False
> +        ntpconf = []
> +        fd = open("/etc/ntp.conf", "r")
> +        for line in fd:
> +            opt = line.split()
> +            if len(opt) < 1:
> +                ntpconf.append(line)
> +                continue
> +
> +            if opt[0] == "server":
> +                match = False
> +                for srv in srv_vals:
> +                    if opt[1] == srv:
> +                        match = True
> +                        break
> +                if match:
> +                    srv_vals.remove(srv)
> +                else:
> +                    file_changed = True
> +                    line = ""
> +            elif opt[0] == "fudge":
> +                if opt[0:4] == fudge[0:4]:
> +                    fudge_present = True
> +                else:
> +                    file_changed = True
> +                    line = ""
> +
> +            ntpconf.append(line)
> +
> +        if file_changed or len(srv_vals) != 0 or not fudge_present:
> +            fd = open("/etc/ntp.conf", "w")
> +            for line in ntpconf:
> +                fd.write(line)
> +            fd.write("\n### Added by IPA Installer ###\n")
> +            if len(srv_vals) != 0:
> +                for srv in srv_vals:
> +                    fd.write("server "+srv+"\n")
> +            if not fudge_present:
> +                fd.write("fudge 127.127.1.0 stratum 10\n")
> +            fd.close()
> +
> +        #read in memory, find OPTIONS, check/change it, then overwrite file
> +        needopts = [ {'val':'-x', 'need':True},
> +                     {'val':'-g', 'need':True} ]
> +        fd = open("/etc/sysconfig/ntpd", "r")
> +        lines = fd.readlines()
> +        fd.close()
> +        for line in lines:
> +            sline = line.strip()
> +            if not sline.startswith('OPTIONS'):
> +                continue
> +            sline = sline.replace('"', '')
> +            for opt in needopts:
> +                if sline.find(opt['val']) != -1:
> +                    opt['need'] = False
> +
> +        newopts = []
> +        for opt in needopts:
> +            if opt['need']:
> +                newopts.append(opt['val'])
> +
> +        done = False
> +        if newopts:
> +            fd = open("/etc/sysconfig/ntpd", "w")
> +            for line in lines:
> +                if not done:
> +                    sline = line.strip()
> +                    if not sline.startswith('OPTIONS'):
> +                        fd.write(line)
> +                        continue
> +                    sline = sline.replace('"', '')
> +                    (variable, opts) = sline.split('=', 1)
> +                    fd.write('OPTIONS="%s %s"\n' % (opts, ' '.join(newopts)))
> +                    done = True
> +                else:
> +                    fd.write(line)
> +            fd.close()
> +
> +    def __stop(self):
> +        self.backup_state("running", self.is_running())
> +        self.stop()
> +
> +    def __start(self):
> +        self.start()
> +
> +    def __enable(self):
> +        self.backup_state("enabled", self.is_enabled())
> +        self.enable()
> +
> +    def create_instance(self):
> +
> +        # we might consider setting the date manually using ntpd -qg in case
> +        # the current time is very far off.
> +
> +        self.step("stopping ntpd", self.__stop)
> +        self.step("writing configuration", self.__write_config)
> +        self.step("configuring ntpd to start on boot", self.__enable)
> +        self.step("starting ntpd", self.__start)
> +
> +        self.start_creation("Configuring ntpd")
> +
> +    def uninstall(self):
> +        if self.is_configured():
> +            self.print_msg("Unconfiguring %s" % self.service_name)
> +
> +        running = self.restore_state("running")
> +        enabled = self.restore_state("enabled")
> +
> +        if not running is None:
> +            self.stop()
> +
> +        try:
> +            self.fstore.restore_file("/etc/ntp.conf")
> +        except ValueError, error:
> +            logging.debug(error)
> +            pass
> +
> +        if not enabled is None and not enabled:
> +            self.disable()
> +
> +        if not running is None and running:
> +            self.start()
> diff --git a/ipapython/platform/base/instances/service.py 
> b/ipapython/platform/base/instances/service.py
> new file mode 100644
> index 0000000..2fd15d8
> --- /dev/null
> +++ b/ipapython/platform/base/instances/service.py
> @@ -0,0 +1,330 @@
> +# Authors: Karl MacMillan <kmacmil...@mentalrootkit.com>
> +#
> +# Copyright (C) 2007  Red Hat
> +# see file 'COPYING' for use and warranty information
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +#
> +
> +import logging, sys
> +import os, socket
> +import tempfile
> +from ipapython import sysrestore
> +from ipapython import ipautil
> +from ipapython import services as ipaservices
> +from ipalib import errors
> +import ldap
> +from ipaserver import ipaldap
> +import base64
> +import time
> +import datetime
> +from ipaserver.install import installutils
> +
> +CACERT = "/etc/ipa/ca.crt"
> +
> +SERVICE_LIST = {
> +    'KDC':('krb5kdc', 10),
> +    'KPASSWD':('ipa_kpasswd', 20),
> +    'DNS':('named', 30),
> +    'HTTP':('httpd', 40),
> +    'CA':('pki-cad', 50)
> +}
> +
> +def print_msg(message, output_fd=sys.stdout):
> +    logging.debug(message)
> +    output_fd.write(message)
> +    output_fd.write("\n")
> +
> +
> +class Service(object):
> +    def __init__(self, service_name, sstore=None, dm_password=None):
> +        self.service_name = service_name
> +        self.service = ipaservices.service(service_name)
> +        self.steps = []
> +        self.output_fd = sys.stdout
> +        self.dm_password = dm_password
> +
> +        self.fqdn = socket.gethostname()
> +        self.admin_conn = None
> +
> +        if sstore:
> +            self.sstore = sstore
> +        else:
> +            self.sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
> +
> +        self.realm = None
> +        self.suffix = None
> +        self.principal = None
> +        self.dercert = None
> +
> +    def ldap_connect(self):
> +        self.admin_conn = self.__get_conn(self.fqdn, self.dm_password)
> +
> +    def ldap_disconnect(self):
> +        self.admin_conn.unbind()
> +        self.admin_conn = None
> +
> +    def _ldap_mod(self, ldif, sub_dict = None):
> +
> +        pw_name = None
> +        fd = None
> +        path = ipautil.SHARE_DIR + ldif
> +        hostname = installutils.get_fqdn()
> +        nologlist=[]
> +
> +        if sub_dict is not None:
> +            txt = ipautil.template_file(path, sub_dict)
> +            fd = ipautil.write_tmp_file(txt)
> +            path = fd.name
> +
> +            # do not log passwords
> +            if sub_dict.has_key('PASSWORD'):
> +                nologlist.append(sub_dict['PASSWORD'])
> +            if sub_dict.has_key('RANDOM_PASSWORD'):
> +                nologlist.append(sub_dict['RANDOM_PASSWORD'])
> +
> +        if self.dm_password:
> +            [pw_fd, pw_name] = tempfile.mkstemp()
> +            os.write(pw_fd, self.dm_password)
> +            os.close(pw_fd)
> +            auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name]
> +        else:
> +            auth_parms = ["-Y", "GSSAPI"]
> +
> +        args = ["/usr/bin/ldapmodify", "-h", hostname, "-v", "-f", path]
> +        args += auth_parms
> +
> +        try:
> +            try:
> +                ipautil.run(args, nolog=nologlist)
> +            except ipautil.CalledProcessError, e:
> +                logging.critical("Failed to load %s: %s" % (ldif, str(e)))
> +        finally:
> +            if pw_name:
> +                os.remove(pw_name)
> +
> +        if fd is not None:
> +            fd.close()
> +
> +    def move_service(self, principal):
> +        """
> +        Used to move a principal entry created by kadmin.local from
> +        cn=kerberos to cn=services
> +        """
> +
> +        dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, 
> self.realm, self.suffix)
> +        try:
> +            entry = self.admin_conn.getEntry(dn, ldap.SCOPE_BASE)
> +        except errors.NotFound:
> +            # There is no service in the wrong location, nothing to do.
> +            # This can happen when installing a replica
> +            return
> +        newdn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % 
> (principal, self.suffix)
> +        hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, 
> self.suffix)
> +        self.admin_conn.deleteEntry(dn)
> +        entry.dn = newdn
> +        classes = entry.getValues("objectclass")
> +        classes = classes + ["ipaobject", "ipaservice", "pkiuser"]
> +        entry.setValues("objectclass", list(set(classes)))
> +        entry.setValue("ipauniqueid", 'autogenerate')
> +        entry.setValue("managedby", hostdn)
> +        self.admin_conn.addEntry(entry)
> +        return newdn
> +
> +    def add_simple_service(self, principal):
> +        """
> +        Add a very basic IPA service.
> +
> +        The principal needs to be fully-formed: service/host@REALM
> +        """
> +        if not self.admin_conn:
> +            self.ldap_connect()
> +
> +        dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, 
> self.suffix)
> +        hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, 
> self.suffix)
> +        entry = ipaldap.Entry(dn)
> +        entry.setValues("objectclass", ["krbprincipal", "krbprincipalaux", 
> "krbticketpolicyaux", "ipaobject", "ipaservice", "pkiuser"])
> +        entry.setValue("krbprincipalname", principal)
> +        entry.setValue("ipauniqueid", 'autogenerate')
> +        entry.setValue("managedby", hostdn)
> +        self.admin_conn.addEntry(entry)
> +        return dn
> +
> +    def add_cert_to_service(self):
> +        """
> +        Add a certificate to a service
> +
> +        This server cert should be in DER format.
> +        """
> +
> +        if not self.admin_conn:
> +            self.ldap_connect()
> +
> +        dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % 
> (self.principal, self.suffix)
> +        mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
> +        try:
> +            self.admin_conn.modify_s(dn, mod)
> +        except Exception, e:
> +            logging.critical("Could not add certificate to service %s entry: 
> %s" % (self.principal, str(e)))
> +
> +    def is_configured(self):
> +        return self.sstore.has_state(self.service_name)
> +
> +    def set_output(self, fd):
> +        self.output_fd = fd
> +
> +    def stop(self, instance_name="", capture_output=True):
> +        self.service.stop(instance_name, capture_output=capture_output)
> +
> +    def start(self, instance_name="", capture_output=True):
> +        self.service.start(instance_name, capture_output=capture_output)
> +
> +    def restart(self, instance_name="", capture_output=True):
> +        self.service.restart(instance_name, capture_output=capture_output)
> +
> +    def is_running(self):
> +        return self.service.is_running()
> +
> +    def install(self):
> +        self.service.install()
> +
> +    def remove(self):
> +        self.service.remove()
> +
> +    def enable(self):
> +        self.service.enable()
> +
> +    def disable(self):
> +        self.service.disable()
> +
> +    def is_enabled(self):
> +        return self.service.is_enabled()
> +
> +    def backup_state(self, key, value):
> +        self.sstore.backup_state(self.service_name, key, value)
> +
> +    def restore_state(self, key):
> +        return self.sstore.restore_state(self.service_name, key)
> +
> +    def print_msg(self, message):
> +        print_msg(message, self.output_fd)
> +
> +    def step(self, message, method):
> +        self.steps.append((message, method))
> +
> +    def start_creation(self, message, runtime=-1):
> +        if runtime > 0:
> +            plural=''
> +            est = time.localtime(runtime)
> +            if est.tm_min > 0:
> +                if est.tm_min > 1:
> +                    plural = 's'
> +                if est.tm_sec > 0:
> +                    self.print_msg('%s: Estimated time %d minute%s %d 
> seconds' % (message, est.tm_min, plural, est.tm_sec))
> +                else:
> +                    self.print_msg('%s: Estimated time %d minute%s' % 
> (message, est.tm_min, plural))
> +            else:
> +                if est.tm_sec > 1:
> +                    plural = 's'
> +                self.print_msg('%s: Estimated time %d second%s' % (message, 
> est.tm_sec, plural))
> +        else:
> +            self.print_msg(message)
> +
> +        step = 0
> +        for (message, method) in self.steps:
> +            self.print_msg("  [%d/%d]: %s" % (step+1, len(self.steps), 
> message))
> +            s = datetime.datetime.now()
> +            method()
> +            e = datetime.datetime.now()
> +            d = e - s
> +            logging.debug("  duration: %d seconds" % d.seconds)
> +            step += 1
> +
> +        self.print_msg("done configuring %s." % self.service_name)
> +
> +        self.steps = []
> +
> +    def __get_conn(self, fqdn, dm_password):
> +        # If we are passed a password we'll use it as the DM password
> +        # otherwise we'll do a GSSAPI bind.
> +        try:
> +#            conn = ipaldap.IPAdmin(fqdn, port=636, cacert=CACERT)
> +            conn = ipaldap.IPAdmin(fqdn, port=389)
> +            if dm_password:
> +                conn.do_simple_bind(bindpw=dm_password)
> +            else:
> +                conn.do_sasl_gssapi_bind()
> +        except Exception, e:
> +            logging.debug("Could not connect to the Directory Server on %s: 
> %s" % (fqdn, str(e)))
> +            raise e
> +
> +        return conn
> +
> +    def ldap_enable(self, name, fqdn, dm_password, ldap_suffix):
> +        self.disable()
> +        conn = self.__get_conn(fqdn, dm_password)
> +
> +        entry_name = "cn=%s,cn=%s,%s,%s" % (name, fqdn,
> +                                            "cn=masters,cn=ipa,cn=etc",
> +                                            ldap_suffix)
> +        order = SERVICE_LIST[name][1]
> +        entry = ipaldap.Entry(entry_name)
> +        entry.setValues("objectclass",
> +                        "nsContainer", "ipaConfigObject")
> +        entry.setValues("cn", name)
> +        entry.setValues("ipaconfigstring",
> +                        "enabledService", "startOrder " + str(order))
> +
> +        try:
> +            conn.add_s(entry)
> +        except ldap.ALREADY_EXISTS, e:
> +            logging.critical("failed to add %s Service startup entry" % name)
> +            raise e
> +
> +class SimpleServiceInstance(Service):
> +    def create_instance(self, gensvc_name=None, fqdn=None, dm_password=None, 
> ldap_suffix=None):
> +        self.gensvc_name = gensvc_name
> +        self.fqdn = fqdn
> +        self.dm_password = dm_password
> +        self.suffix = ldap_suffix
> +
> +        self.step("starting %s " % self.service_name, self.__start)
> +        self.step("configuring %s to start on boot" % self.service_name, 
> self.__enable)
> +        self.start_creation("Configuring %s" % self.service_name)
> +
> +    def __start(self):
> +        self.backup_state("running", self.is_running())
> +        self.restart()
> +
> +    def __enable(self):
> +        self.enable()
> +        self.backup_state("enabled", self.is_enabled())
> +        if self.gensvc_name == None:
> +            self.enable()
> +        else:
> +            self.ldap_enable(self.gensvc_name, self.fqdn,
> +                             self.dm_password, self.suffix)
> +
> +    def uninstall(self):
> +        if self.is_configured():
> +            self.print_msg("Unconfiguring %s" % self.service_name)
> +
> +        running = self.restore_state("running")
> +        enabled = not self.restore_state("enabled")
> +
> +        if not running is None and not running:
> +            self.stop()
> +        if not enabled is None and not enabled:
> +            self.disable()
> +            self.remove()
> diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
> deleted file mode 100644
> index 2fd15d8..0000000
> --- a/ipaserver/install/service.py
> +++ /dev/null
> @@ -1,330 +0,0 @@
> -# Authors: Karl MacMillan <kmacmil...@mentalrootkit.com>
> -#
> -# Copyright (C) 2007  Red Hat
> -# see file 'COPYING' for use and warranty information
> -#
> -# This program is free software; you can redistribute it and/or modify
> -# it under the terms of the GNU General Public License as published by
> -# the Free Software Foundation, either version 3 of the License, or
> -# (at your option) any later version.
> -#
> -# This program is distributed in the hope that it will be useful,
> -# but WITHOUT ANY WARRANTY; without even the implied warranty of
> -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> -# GNU General Public License for more details.
> -#
> -# You should have received a copy of the GNU General Public License
> -# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> -#
> -
> -import logging, sys
> -import os, socket
> -import tempfile
> -from ipapython import sysrestore
> -from ipapython import ipautil
> -from ipapython import services as ipaservices
> -from ipalib import errors
> -import ldap
> -from ipaserver import ipaldap
> -import base64
> -import time
> -import datetime
> -from ipaserver.install import installutils
> -
> -CACERT = "/etc/ipa/ca.crt"
> -
> -SERVICE_LIST = {
> -    'KDC':('krb5kdc', 10),
> -    'KPASSWD':('ipa_kpasswd', 20),
> -    'DNS':('named', 30),
> -    'HTTP':('httpd', 40),
> -    'CA':('pki-cad', 50)
> -}
> -
> -def print_msg(message, output_fd=sys.stdout):
> -    logging.debug(message)
> -    output_fd.write(message)
> -    output_fd.write("\n")
> -
> -
> -class Service(object):
> -    def __init__(self, service_name, sstore=None, dm_password=None):
> -        self.service_name = service_name
> -        self.service = ipaservices.service(service_name)
> -        self.steps = []
> -        self.output_fd = sys.stdout
> -        self.dm_password = dm_password
> -
> -        self.fqdn = socket.gethostname()
> -        self.admin_conn = None
> -
> -        if sstore:
> -            self.sstore = sstore
> -        else:
> -            self.sstore = sysrestore.StateFile('/var/lib/ipa/sysrestore')
> -
> -        self.realm = None
> -        self.suffix = None
> -        self.principal = None
> -        self.dercert = None
> -
> -    def ldap_connect(self):
> -        self.admin_conn = self.__get_conn(self.fqdn, self.dm_password)
> -
> -    def ldap_disconnect(self):
> -        self.admin_conn.unbind()
> -        self.admin_conn = None
> -
> -    def _ldap_mod(self, ldif, sub_dict = None):
> -
> -        pw_name = None
> -        fd = None
> -        path = ipautil.SHARE_DIR + ldif
> -        hostname = installutils.get_fqdn()
> -        nologlist=[]
> -
> -        if sub_dict is not None:
> -            txt = ipautil.template_file(path, sub_dict)
> -            fd = ipautil.write_tmp_file(txt)
> -            path = fd.name
> -
> -            # do not log passwords
> -            if sub_dict.has_key('PASSWORD'):
> -                nologlist.append(sub_dict['PASSWORD'])
> -            if sub_dict.has_key('RANDOM_PASSWORD'):
> -                nologlist.append(sub_dict['RANDOM_PASSWORD'])
> -
> -        if self.dm_password:
> -            [pw_fd, pw_name] = tempfile.mkstemp()
> -            os.write(pw_fd, self.dm_password)
> -            os.close(pw_fd)
> -            auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name]
> -        else:
> -            auth_parms = ["-Y", "GSSAPI"]
> -
> -        args = ["/usr/bin/ldapmodify", "-h", hostname, "-v", "-f", path]
> -        args += auth_parms
> -
> -        try:
> -            try:
> -                ipautil.run(args, nolog=nologlist)
> -            except ipautil.CalledProcessError, e:
> -                logging.critical("Failed to load %s: %s" % (ldif, str(e)))
> -        finally:
> -            if pw_name:
> -                os.remove(pw_name)
> -
> -        if fd is not None:
> -            fd.close()
> -
> -    def move_service(self, principal):
> -        """
> -        Used to move a principal entry created by kadmin.local from
> -        cn=kerberos to cn=services
> -        """
> -
> -        dn = "krbprincipalname=%s,cn=%s,cn=kerberos,%s" % (principal, 
> self.realm, self.suffix)
> -        try:
> -            entry = self.admin_conn.getEntry(dn, ldap.SCOPE_BASE)
> -        except errors.NotFound:
> -            # There is no service in the wrong location, nothing to do.
> -            # This can happen when installing a replica
> -            return
> -        newdn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % 
> (principal, self.suffix)
> -        hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, 
> self.suffix)
> -        self.admin_conn.deleteEntry(dn)
> -        entry.dn = newdn
> -        classes = entry.getValues("objectclass")
> -        classes = classes + ["ipaobject", "ipaservice", "pkiuser"]
> -        entry.setValues("objectclass", list(set(classes)))
> -        entry.setValue("ipauniqueid", 'autogenerate')
> -        entry.setValue("managedby", hostdn)
> -        self.admin_conn.addEntry(entry)
> -        return newdn
> -
> -    def add_simple_service(self, principal):
> -        """
> -        Add a very basic IPA service.
> -
> -        The principal needs to be fully-formed: service/host@REALM
> -        """
> -        if not self.admin_conn:
> -            self.ldap_connect()
> -
> -        dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % (principal, 
> self.suffix)
> -        hostdn = "fqdn=%s,cn=computers,cn=accounts,%s" % (self.fqdn, 
> self.suffix)
> -        entry = ipaldap.Entry(dn)
> -        entry.setValues("objectclass", ["krbprincipal", "krbprincipalaux", 
> "krbticketpolicyaux", "ipaobject", "ipaservice", "pkiuser"])
> -        entry.setValue("krbprincipalname", principal)
> -        entry.setValue("ipauniqueid", 'autogenerate')
> -        entry.setValue("managedby", hostdn)
> -        self.admin_conn.addEntry(entry)
> -        return dn
> -
> -    def add_cert_to_service(self):
> -        """
> -        Add a certificate to a service
> -
> -        This server cert should be in DER format.
> -        """
> -
> -        if not self.admin_conn:
> -            self.ldap_connect()
> -
> -        dn = "krbprincipalname=%s,cn=services,cn=accounts,%s" % 
> (self.principal, self.suffix)
> -        mod = [(ldap.MOD_ADD, 'userCertificate', self.dercert)]
> -        try:
> -            self.admin_conn.modify_s(dn, mod)
> -        except Exception, e:
> -            logging.critical("Could not add certificate to service %s entry: 
> %s" % (self.principal, str(e)))
> -
> -    def is_configured(self):
> -        return self.sstore.has_state(self.service_name)
> -
> -    def set_output(self, fd):
> -        self.output_fd = fd
> -
> -    def stop(self, instance_name="", capture_output=True):
> -        self.service.stop(instance_name, capture_output=capture_output)
> -
> -    def start(self, instance_name="", capture_output=True):
> -        self.service.start(instance_name, capture_output=capture_output)
> -
> -    def restart(self, instance_name="", capture_output=True):
> -        self.service.restart(instance_name, capture_output=capture_output)
> -
> -    def is_running(self):
> -        return self.service.is_running()
> -
> -    def install(self):
> -        self.service.install()
> -
> -    def remove(self):
> -        self.service.remove()
> -
> -    def enable(self):
> -        self.service.enable()
> -
> -    def disable(self):
> -        self.service.disable()
> -
> -    def is_enabled(self):
> -        return self.service.is_enabled()
> -
> -    def backup_state(self, key, value):
> -        self.sstore.backup_state(self.service_name, key, value)
> -
> -    def restore_state(self, key):
> -        return self.sstore.restore_state(self.service_name, key)
> -
> -    def print_msg(self, message):
> -        print_msg(message, self.output_fd)
> -
> -    def step(self, message, method):
> -        self.steps.append((message, method))
> -
> -    def start_creation(self, message, runtime=-1):
> -        if runtime > 0:
> -            plural=''
> -            est = time.localtime(runtime)
> -            if est.tm_min > 0:
> -                if est.tm_min > 1:
> -                    plural = 's'
> -                if est.tm_sec > 0:
> -                    self.print_msg('%s: Estimated time %d minute%s %d 
> seconds' % (message, est.tm_min, plural, est.tm_sec))
> -                else:
> -                    self.print_msg('%s: Estimated time %d minute%s' % 
> (message, est.tm_min, plural))
> -            else:
> -                if est.tm_sec > 1:
> -                    plural = 's'
> -                self.print_msg('%s: Estimated time %d second%s' % (message, 
> est.tm_sec, plural))
> -        else:
> -            self.print_msg(message)
> -
> -        step = 0
> -        for (message, method) in self.steps:
> -            self.print_msg("  [%d/%d]: %s" % (step+1, len(self.steps), 
> message))
> -            s = datetime.datetime.now()
> -            method()
> -            e = datetime.datetime.now()
> -            d = e - s
> -            logging.debug("  duration: %d seconds" % d.seconds)
> -            step += 1
> -
> -        self.print_msg("done configuring %s." % self.service_name)
> -
> -        self.steps = []
> -
> -    def __get_conn(self, fqdn, dm_password):
> -        # If we are passed a password we'll use it as the DM password
> -        # otherwise we'll do a GSSAPI bind.
> -        try:
> -#            conn = ipaldap.IPAdmin(fqdn, port=636, cacert=CACERT)
> -            conn = ipaldap.IPAdmin(fqdn, port=389)
> -            if dm_password:
> -                conn.do_simple_bind(bindpw=dm_password)
> -            else:
> -                conn.do_sasl_gssapi_bind()
> -        except Exception, e:
> -            logging.debug("Could not connect to the Directory Server on %s: 
> %s" % (fqdn, str(e)))
> -            raise e
> -
> -        return conn
> -
> -    def ldap_enable(self, name, fqdn, dm_password, ldap_suffix):
> -        self.disable()
> -        conn = self.__get_conn(fqdn, dm_password)
> -
> -        entry_name = "cn=%s,cn=%s,%s,%s" % (name, fqdn,
> -                                            "cn=masters,cn=ipa,cn=etc",
> -                                            ldap_suffix)
> -        order = SERVICE_LIST[name][1]
> -        entry = ipaldap.Entry(entry_name)
> -        entry.setValues("objectclass",
> -                        "nsContainer", "ipaConfigObject")
> -        entry.setValues("cn", name)
> -        entry.setValues("ipaconfigstring",
> -                        "enabledService", "startOrder " + str(order))
> -
> -        try:
> -            conn.add_s(entry)
> -        except ldap.ALREADY_EXISTS, e:
> -            logging.critical("failed to add %s Service startup entry" % name)
> -            raise e
> -
> -class SimpleServiceInstance(Service):
> -    def create_instance(self, gensvc_name=None, fqdn=None, dm_password=None, 
> ldap_suffix=None):
> -        self.gensvc_name = gensvc_name
> -        self.fqdn = fqdn
> -        self.dm_password = dm_password
> -        self.suffix = ldap_suffix
> -
> -        self.step("starting %s " % self.service_name, self.__start)
> -        self.step("configuring %s to start on boot" % self.service_name, 
> self.__enable)
> -        self.start_creation("Configuring %s" % self.service_name)
> -
> -    def __start(self):
> -        self.backup_state("running", self.is_running())
> -        self.restart()
> -
> -    def __enable(self):
> -        self.enable()
> -        self.backup_state("enabled", self.is_enabled())
> -        if self.gensvc_name == None:
> -            self.enable()
> -        else:
> -            self.ldap_enable(self.gensvc_name, self.fqdn,
> -                             self.dm_password, self.suffix)
> -
> -    def uninstall(self):
> -        if self.is_configured():
> -            self.print_msg("Unconfiguring %s" % self.service_name)
> -
> -        running = self.restore_state("running")
> -        enabled = not self.restore_state("enabled")
> -
> -        if not running is None and not running:
> -            self.stop()
> -        if not enabled is None and not enabled:
> -            self.disable()
> -            self.remove()
> -- 
> 1.7.9
> 

> From 9456151da78f9228045d2592e12a2d41f0c957a7 Mon Sep 17 00:00:00 2001
> From: Krzysztof Klimonda <kklimo...@syntaxhighlighted.com>
> Date: Sun, 26 Feb 2012 16:42:03 +0100
> Subject: [PATCH 2/2] initial import of debian platform modules
> 
> ---
>  ipapython/platform/debian/__init__.py           |   54 ++++++++++++++
>  ipapython/platform/debian/auth.py               |   21 ++++++
>  ipapython/platform/debian/instances/__init__.py |    1 +
>  ipapython/platform/debian/instances/http.py     |   19 +++++
>  ipapython/platform/debian/instances/ntp.py      |   36 +++++++++
>  ipapython/platform/debian/service.py            |   88 
> +++++++++++++++++++++++
>  6 files changed, 219 insertions(+), 0 deletions(-)
>  create mode 100644 ipapython/platform/debian/__init__.py
>  create mode 100644 ipapython/platform/debian/auth.py
>  create mode 100644 ipapython/platform/debian/instances/__init__.py
>  create mode 100644 ipapython/platform/debian/instances/http.py
>  create mode 100644 ipapython/platform/debian/instances/ntp.py
>  create mode 100644 ipapython/platform/debian/service.py
> 
> diff --git a/ipapython/platform/debian/__init__.py 
> b/ipapython/platform/debian/__init__.py
> new file mode 100644
> index 0000000..92f09f2
> --- /dev/null
> +++ b/ipapython/platform/debian/__init__.py
> @@ -0,0 +1,54 @@
> +import os
> +
> +from .auth import DebianAuthConfig
> +from .instances import DebianNTPInstance
> +from .service import DebianService
> +from .service import DebianServices
> +
> +auth = ["authconfig"]
> +services = ["service", "knownservices"]
> +instances = ["ntpinstance"]
> +utils = ["restore_context", "backup_and_replace_hostname"]
> +
> +__all__ = auth + services + instances + utils
> +
> +authconfig = DebianAuthConfig
> +service = DebianService
> +ntpinstance = DebianNTPInstance
> +knownservices = DebianServices()
> +
> +def restore_context(filepath):
> +    """
> +    restore security context on the file path
> +    SELinux equivalent is /sbin/restorecon <filepath>
> +
> +    restorecon's return values are not reliable so we have to
> +    ignore them (BZ #739604). SELinux is optional on Debian systems
> +    so we have to check if /sbin/restorecon exists before calling it.
> +
> +    ipautil.run() will do the logging.
> +    """
> +    if os.path.exists("/sbin/restorecon"):
> +        ipautil.run(["/sbin/restorecon", filepath], raiseonerr=False)
> +
> +
> +def backup_and_replace_hostname(fstore, statestore, hostname):
> +    network_filename = "/etc/hostname"
> +    # Backup original /etc/hostname
> +    fstore.backup_file(network_filename)
> +
> +    # Write new configuration
> +    f = open(network_filename, 'w')
> +    f.write(hostname + "\n")
> +    f.close()
> +
> +    try:
> +        ipautil.run(['/bin/hostname', hostname])
> +    except ipautil.CalledProcessError, e:
> +        print >>sys.stderr, "Failed to set this machine hostname to %s 
> (%s)." % (hostname, str(e))
> +
> +    # For SE Linux environments it is important to reset SE labels to the 
> expected ones
> +    try:
> +        restore_context(network_filename)
> +    except ipautil.CalledProcessError, e:
> +        print >>sys.stderr, "Failed to set permissions for %s (%s)." % 
> (network_filename, str(e))
> diff --git a/ipapython/platform/debian/auth.py 
> b/ipapython/platform/debian/auth.py
> new file mode 100644
> index 0000000..1029d6f
> --- /dev/null
> +++ b/ipapython/platform/debian/auth.py
> @@ -0,0 +1,21 @@
> +from ..base import AuthConfig
> +
> +class DebianAuthConfig(AuthConfig):
> +    """
> +    Debian implementation of the AuthConfig class.
> +
> +    Debian doesn't provide a single application for changing both
> +    nss and pam configuration. PAM can be configured using debconf but there
> +    is currently no such solution for updating NSS database and every package
> +    does it by itself.
> +
> +    We'll have to play a catch-up game with the rest of the FreeIPA project
> +    filtering out .enable() and .disable() calls that are useless for us,
> +    and making the best out of the rest of them.
> +    """
> +
> +    # a list of pam config items we can use with pam-auth-update
> +    _pam_configs = ["ldap", "krb5", "mkhomedir"]
> +
> +    def execute(self):
> +        raise NotImplementedError
> \ No newline at end of file
> diff --git a/ipapython/platform/debian/instances/__init__.py 
> b/ipapython/platform/debian/instances/__init__.py
> new file mode 100644
> index 0000000..b571a06
> --- /dev/null
> +++ b/ipapython/platform/debian/instances/__init__.py
> @@ -0,0 +1 @@
> +from .ntp import DebianNTPInstance
> diff --git a/ipapython/platform/debian/instances/http.py 
> b/ipapython/platform/debian/instances/http.py
> new file mode 100644
> index 0000000..419d05a
> --- /dev/null
> +++ b/ipapython/platform/debian/instances/http.py
> @@ -0,0 +1,19 @@
> +import ipautil
> +import installutils
> +
> +NSS_CONF = '/etc/apache2/mods-available/nss.conf'
> +
> +class DebianHTTPInstance(object):
> +    def __init__(self):
> +        pass
> +
> +    def __disable_mod_ssl(self):
> +     ipautil.run(['/usr/sbin/a2dismod', 'ssl'])
> +
> +
> +    def __set_mod_nss_port(self):
> +     self.fstore.backup(NSS_CONF)
> +     if installutils.update_file(NSS_CONF, '8443', '443')
> +
> +    def __set_mod_nss_nickname(self, nickname):
> +     installutils.set_directive(NSS_CONF, 'NSSNickName', nickname)
> \ No newline at end of file
> diff --git a/ipapython/platform/debian/instances/ntp.py 
> b/ipapython/platform/debian/instances/ntp.py
> new file mode 100644
> index 0000000..eb06ce4
> --- /dev/null
> +++ b/ipapython/platform/debian/instances/ntp.py
> @@ -0,0 +1,36 @@
> +# Authors: Krzysztof Klimonda <kklimo...@ubuntu.com>
> +#
> +# Copyright (C) 2012  Krzysztof Klimonda
> +# see file 'COPYING' for use and warranty information
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation, either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +#
> +
> +from ..base import NTPInstance
> +
> +class DebianNTPInstance(NTPInstance):
> +    _config_files = ["/etc/ntp.conf", "/etc/default/ntp"]
> +
> +    def __init__(self, fsstore=None):
> +        super(NTPInstance, self).__init__(fsstore)
> +
> +    def __get_distrib_pools(self):
> +     pools = []
> +     for num in xrange(1, 2):
> +             pools.append('{0}.{1}.pool.ntp.org'.format(num, 'debian'))
> +     return pools
> +
> +    def __write_ntp_opts(self):
> +     """Debian specific method override."""
> +     self.__write_ntp_opts_generic('/etc/default/ntp', 'NTPD_OPTS', '\'')
> \ No newline at end of file
> diff --git a/ipapython/platform/debian/service.py 
> b/ipapython/platform/debian/service.py
> new file mode 100644
> index 0000000..5084f7a
> --- /dev/null
> +++ b/ipapython/platform/debian/service.py
> @@ -0,0 +1,88 @@
> +from ..base import KnownServices
> +from ..base import PlatformService
> +from ..base import wellknownservices
> +
> +# mappings between rhel-style and debian-style service names
> +_service_mappings = {
> +        'messagebus': 'dbus',
> +        'ntpd': 'ntp'
> +}
> +
> +class DebianService(PlatformService):
> +    def __init__(self, service_name):
> +        try:
> +            service_name = _service_mappings[service_name]
> +        except KeyError:
> +            pass
> +        super(PlatformService, self).__init__(self)
> +
> +    def _call_service(self, action, instance_name, capture_output):
> +        cmdline = ['/usr/sbin/service', self.service_name, action, 
> instance_name]
> +        return ipautil.run(cmdline, capture_output=capture_output)
> +        
> +    def stop(self, instance_name='', capture_output=True):
> +        self._call_service('stop', instance_name, capture_output)
> +
> +    def start(self, instance_name='', capture_output=True):
> +        self._call_service('start', instance_name, capture_output)
> +
> +    def restart(self, instance_name='', capture_output=True):
> +        self._call_service('restart', instance_name, capture_output)
> +
> +    def is_running(self, instance_name=""):
> +        ret = True
> +        try:
> +            (sout, serr, rcode) = self._call_service("status", instance_name)
> +            if sout.find("NOT running") >= 0:
> +                ret = False
> +            if sout.find("stop") >= 0:
> +                ret = False
> +        except ipautil.CalledProcessError:
> +                ret = False
> +        return ret
> +
> +    def is_installed(self):
> +        installed = True
> +        try:
> +            ipautil.run(["/usr/sbin/service", self.service_name, "status"])
> +        except ipautil.CalledProcessError, e:
> +            if e.returncode == 1:
> +                # service is not installed or there is other serious issue
> +                installed = False
> +        return installed
> +
> +    def is_enabled(self):
> +        ret = True
> +        try:
> +            (sout,serr,rcode) = ipautil.run(["/sbin/chkconfig", 
> self.service_name])
> +            if sout.find("off") >= 0:
> +                ret = False
> +            if sout.find("unknown service") >= 0:
> +                ret = False
> +        except ipautil.CalledProcessError:
> +                ret = False
> +        return ret
> +
> +    def enable(self):
> +        ipautil.run(["/sbin/chkconfig", self.service_name, "on"])
> +
> +    def disable(self):
> +        ipautil.run(["/sbin/chkconfig", self.service_name, "off"])
> +
> +    def install(self):
> +        ipautil.run(["/sbin/chkconfig", "--add", self.service_name])
> +
> +    def remove(self):
> +        ipautil.run(["/sbin/chkconfig", "--del", self.service_name])
> +
> +
> +class DebianServices(KnownServices):
> +    def __init__(self):
> +        services = dict()
> +        for service in wellknownservices:
> +            try:
> +                service = DebianService(_service_mappings[service])
> +            except KeyError:
> +                service = DebianService(service)
> +            services.append(service)
> +        super(DebianServices, self).__init__(services)
> \ No newline at end of file
> -- 
> 1.7.9
> 




-- 
/ Alexander Bokovoy

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to