Help us understand the problem. What is going on with this article?

AnsibleでOSのディストリビューションによって挙動を変えるモジュールの実装例

More than 5 years have passed since last update.

AnsibleでOSによって挙動を変えるモジュールの実装例について説明します。

モジュールの作成方法については以下のページを参照してください。

OSによって挙動を変えるモジュール

まず、ディストリビューションではなくOSによって挙動を変える実装について説明します。

例としてuserモジュールソースを見ていきます。

このモジュール内ではUserクラスとそのサブクラスとして FreeBsdUserクラスOpenBSDUserクラスNetBSDUserクラスSunOSクラスAIXクラスが定義されています。

サブクラスではcreate_user()、remove_user()、modify_user()メソッドをオーバーライドしています。

Userクラスのnewメソッドは以下の様な定義になっています。

    def __new__(cls, *args, **kwargs):
        return load_platform_subclass(User, args, kwargs)

load_platform_subclass関数の実装は以下のようになっています。

def load_platform_subclass(cls, *args, **kwargs):
    '''
    used by modules like User to have different implementations based on detected platform.  See User
    module for an example.
    '''

    this_platform = get_platform()
    distribution = get_distribution()
    subclass = None

    # get the most specific superclass for this platform
    if distribution is not None:
        for sc in cls.__subclasses__():
            if sc.distribution is not None and sc.distribution == distribution and sc.platform == this_platform:
                subclass = sc
    if subclass is None:
        for sc in cls.__subclasses__():
            if sc.platform == this_platform and sc.distribution is None:
                subclass = sc
    if subclass is None:
        subclass = cls

    return super(cls, subclass).__new__(subclass)

呼び出したクラスのサブクラスのうち、ディストリビューションとプラットフォームが一致する場合はそのサブクラス、ディストリビューションが未定義な場合 (get_distributionがNoneを返す場合) はプラットフォームが一致するサブクラス、一致するものがない場合は呼び出したクラスのnewを呼ぶようになっています。

あとは、
https://github.com/ansible/ansible/blob/v1.4.1/library/system/user#L1438

    user = User(module)

のように呼び出すことで、実行環境のOSに応じたクラスのインスタンスが生成されるというわけです。

ディストリビューションによって挙動を変えるモジュール

ディストリビューションによって挙動を変えるモジュールの例としては、Ansible 1.4から追加されたhostnameモジュールがあります。ソースは https://github.com/ansible/ansible/blob/v1.4.1/library/system/hostname です。

実はhostnameモジュールは私のプルリクエストが取り込まれたものです。

userモジュールのUserクラスと同様に、
Hostnameクラスとそのサブクラスとして DebianHostnameUbuntuHostnameRedHat5HostnameRedHatHostnameCentOSHostnameFedoraHostnameOpenSUSEHostnameArchHostnameが定義されています。

ただ、DebianとUbuntuのようにホスト名の設定方法が同じディストリビューションもあるので実装を共通化したいと考えました。

通常の継承ベースの考え方ならHostnameのサブクラスにDebianとUbuntuの共通用のクラスを作りたいところです。が、load_platform_subclassは上に引用したような実装になっていて、子孫クラスではなく直下のサブクラスしか探してくれませんし、サブクラスのget_distribution()の戻り値と比較するのでディストリビューションごとにサブクラスを分ける必要があります。

そこで、ホスト名の設定方法については別クラスにすることにしました。UnimplementedStrategyGenericStrategyDebianStrategyRedHatStrategyFedoraStrategyが定義されています。

これらのクラスは
https://github.com/ansible/ansible/blob/v1.4.1/library/system/hostname#L170-L178

class DebianHostname(Hostname):
    platform = 'Linux'
    distribution = 'Debian'
    strategy_class = DebianStrategy

class UbuntuHostname(Hostname):
    platform = 'Linux'
    distribution = 'Ubuntu'
    strategy_class = DebianStrategy

のようにHostnameの各サブクラスで利用するものを設定していて、Hostnameクラスの実装で
https://github.com/ansible/ansible/blob/v1.4.1/library/system/hostname#L90-L100

    def get_current_hostname(self):
        return self.strategy.get_current_hostname()

    def set_current_hostname(self, name):
        self.strategy.set_current_hostname(name)

    def get_permanent_hostname(self):
        return self.strategy.get_permanent_hostname()

    def set_permanent_hostname(self, name):
        self.strategy.set_permanent_hostname(name)

のように呼び出すことによって実装を切り替えています。

汎用的なモジュールを作ったらプルリクエストを送りましょう

AnsibleはPythonで書かれているだけではなく、Pythonの"batteries included"の思想も受け継いでいます。http://www.ansibleworks.com/docs/#modules

Ansible follows a “batteries included” philosophy, so you have a lot of great modules for all manner of IT tasks in the core distribution.

ですので、汎用的なモジュールは喜んで取り込んでもらえます。

2014-05-24追記 サイト更新で上記の記述は無くなっています。Batteries IncludedについてはAnsible for Configuration Managementの最後に書かれています。

Ansible features close to 200 modules in the core distribution, providing a great base to build automation upon. From services and databases to cloud providers, with Ansible you don't have to start from scratch.

https://github.com/ansible/ansible/blob/devel/CONTRIBUTING.md#sharing-a-feature-ideahttps://github.com/ansible/ansible/blob/devel/CONTRIBUTING.md#contributing-code-features-or-bugfixes によると、アイディアについてはgithubでチケットを作るかメーリングリストやIRCで相談して、コードをレビューしてもらうにはプルリクエストを送るのがよいです。

hostnameモジュールを作った時も、メーリングリストでMichael DeHaanさんに
https://groups.google.com/d/msg/ansible-project/komqS4WdqHU/cRfWiMnCR3cJ

The right way to get this reviewed would be to submit a pull request to the github project.

と言われてプルリクエストを送りました。当初はDebian, Ubuntu, RHEL6, CentOSぐらいしか対応していませんでしたが、他のディストリビューションでの動作報告を受けて対応ディストリビューションが増えています。

最初は多少不完全でもレビューを受けて改善していけば取り込んでもらえます。

みなさんも汎用的なモジュールを作ったら、ぜひプルリクエストを送りましょう!

sakura_internet
さくらレンタルサーバ、さくらのVPS、 さくらのクラウド、さくらの専用サーバなどのインターネットサービス・ITプラットフォームを提供しています。
https://www.sakura.ad.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした