service.py の迷宮

service.py では、3つのクラスとサービス起動・停止の為の多数のメソッドが定義 されています。

class Launcher(object):
    def __init__(self):
    def run_server(server):
    def launch_server(self, server):
    def stop(self):
    def wait(self):

class Service(object):
    def __init__(self, host, binary, topic, manager, report_interval=None,
        self.manager_class_name = manager
        manager_class = utils.import_class(self.manager_class_name)
        self.manager = manager_class(host=self.host, *args, **kwargs)
    def start(self):
    def _create_service_ref(self, context):
    def __getattr__(self, key):
    @classmethod
    def create(cls, host=None, binary=None, topic=None, manager=None,
        """Instantiates class and passes back application object.
    def kill(self):
    def stop(self):
    def wait(self):
    def periodic_tasks(self, raise_on_error=False):
    def report_state(self):

class WSGIService(object):
    def __init__(self, name, loader=None):
    def _get_manager(self):
        Use the service name to look up a Manager subclass from the
        configuration and initialize an instance. If no class name
        manager_class_name = FLAGS.get(fl, None)
        if not manager_class_name:
        manager_class = utils.import_class(manager_class_name)
        return manager_class()
    def start(self):
    def stop(self):
    def wait(self):

def serve(*servers):
def wait():

先程の nova-scheduler で呼び出していた service.Service.create(binary=’ nova-scheduler’) を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Service(object):
    @classmethod
    def create(cls, host=None, binary=None, topic=None, manager=None,
               report_interval=None, periodic_interval=None):
        """Instantiates class and passes back application object.

        :param host: defaults to FLAGS.host
        :param binary: defaults to basename of executable
        :param topic: defaults to bin_name - 'nova-' part
        :param manager: defaults to FLAGS.<topic>_manager
        :param report_interval: defaults to FLAGS.report_interval
        :param periodic_interval: defaults to FLAGS.periodic_interval

        """
        if not host:
            host = FLAGS.host
        if not binary:
            binary = os.path.basename(inspect.stack()[-1][1])
        if not topic:
            topic = binary.rpartition('nova-')[2]
        if not manager:
            manager = FLAGS.get('%s_manager' % topic, None)
        if not report_interval:
            report_interval = FLAGS.report_interval
        if not periodic_interval:
            periodic_interval = FLAGS.periodic_interval
        service_obj = cls(host, binary, topic, manager,
                          report_interval, periodic_interval)

        return service_obj

binary 引数が省略された場合、binary 変数は実行したコマンド名(例: /usr/bin/nova-scheduler)から取得されます。 nova-scheduler の場合、binary 引数には ‘nova-scheduler’ がセットされていま すので、

変数名
cls service.Service クラス自体(@classmethod による第一引数)
host nova.conf 中の host パラメータ値(又は自動取得されたホスト名)
binary ‘nova-scheduler’
topic scheduler’
manager nova.conf 中の scheduler_manager パラメータ値
report_interval nova.conf 中の report_intervalt パラメータ値
periodic_interval nova.conf 中の periodic_intervalt パラメータ値

となり、27 行目にて service.Service クラスのコンストラクタが呼ばれ、生成さ れたインスタンスが return されます。イニシエータは以下の通りです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Service(object):
    def __init__(self, host, binary, topic, manager, report_interval=None,
                 periodic_interval=None, *args, **kwargs):
        self.host = host
        self.binary = binary
        self.topic = topic
        self.manager_class_name = manager
        manager_class = utils.import_class(self.manager_class_name)
        self.manager = manager_class(host=self.host, *args, **kwargs)
        self.report_interval = report_interval
        self.periodic_interval = periodic_interval
        super(Service, self).__init__(*args, **kwargs)
        self.saved_args, self.saved_kwargs = args, kwargs
        self.timers = []

8 行目でマネージャクラス(scheduler_manager パラメータ値で指定)自体を取得 し、9 行目でそのインスタンスを取得しています。

インスタンスはその後、nova-scheduler の中の

server = service.Service.create(binary='nova-scheduler')
service.serve(server)
service.wait()

の部分で使用されます。service.serve(server), service.wait() は下記の通りです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# NOTE(vish): the global launcher is to maintain the existing
#             functionality of calling service.serve +
#             service.wait
_launcher = None


def serve(*servers):
    global _launcher
    if not _launcher:
        _launcher = Launcher()
    for server in servers:
        _launcher.launch_server(server)


def wait():
    LOG.debug(_('Full set of FLAGS:'))
    for flag in FLAGS:
        flag_get = FLAGS.get(flag, None)
        # hide flag contents from log if contains a password
        # should use secret flag when switch over to openstack-common
        if ("_password" in flag or "_key" in flag or
                (flag == "sql_connection" and "mysql:" in flag_get)):
            LOG.debug(_('%(flag)s : FLAG SET ') % locals())
        else:
            LOG.debug('%(flag)s : %(flag_get)s' % locals())
    try:
        _launcher.wait()
    except KeyboardInterrupt:
        _launcher.stop()
    rpc.cleanup()

_launcher.launch_server() 以降は Server クラスと Launcher クラスの間で行っ たり来たりするので、説明は割愛しますが、結果として service.Service.start() メソッドが呼び出されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Service(object):
    def start(self):
        vcs_string = version.version_string_with_vcs()
        LOG.audit(_('Starting %(topic)s node (version %(vcs_string)s)'),
                  {'topic': self.topic, 'vcs_string': vcs_string})
        utils.cleanup_file_locks()
        self.manager.init_host()
        self.model_disconnected = False
        ctxt = context.get_admin_context()
        try:
            service_ref = db.service_get_by_args(ctxt,
                                                 self.host,
                                                 self.binary)
            self.service_id = service_ref['id']
        except exception.NotFound:
            self._create_service_ref(ctxt)

        if 'nova-compute' == self.binary:
            self.manager.update_available_resource(ctxt)

        self.conn = rpc.create_connection(new=True)
        LOG.debug(_("Creating Consumer connection for Service %s") %
                  self.topic)

        # Share this same connection for these Consumers
        self.conn.create_consumer(self.topic, self, fanout=False)

        node_topic = '%s.%s' % (self.topic, self.host)
        self.conn.create_consumer(node_topic, self, fanout=False)

        self.conn.create_consumer(self.topic, self, fanout=True)

        # Consume from all consumers in a thread
        self.conn.consume_in_thread()

        if self.report_interval:
            pulse = utils.LoopingCall(self.report_state)
            pulse.start(interval=self.report_interval, now=False)
            self.timers.append(pulse)

        if self.periodic_interval:
            periodic = utils.LoopingCall(self.periodic_tasks)
            periodic.start(interval=self.periodic_interval, now=False)
            self.timers.append(periodic)

この中では以下の処理が実行されます。

  1. ロックファイルの削除
  2. マネージャクラスの init_host() メソッド実行
  3. DB に自サービスの id を問い合わせ(エントリがなければ生成)
  4. (nova-compute の場合)update_available_resource() メソッド実行
  5. RPC の初期化
  6. RPC 用スレッド作成
  7. (report_interval が 0 でなけば)レポート用タイマ設定
  8. (periodic_interval が 0 でなけば)定時処理用タイマ設定

以降、nova-api 以外の nova の各種サービスはタイマーによって定時処理を行いつ つ、RPC によって自身に指示が来るのを待つようになります(イベントドリブン)。

nova-api は RPC を送る事はあっても受ける事はありません。RESTful API にてク ライアントからのリクエストを受け取り、それを RPC で別の nova コンポーネント に送ったりします。

Previous topic

Nova ソースコード構成

Next topic

各サービスのサーバ・クライアント機構

This Page