Ceilometer項目源碼分析----ceilometer-agent-central服務的初始化和啓動

感謝朋友支持本博客,歡迎共同探討交流,由於能力和時間有限,錯誤之處在所難免,歡迎指正!

如果轉載,請保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
郵箱地址:[email protected]

PS:最近沒有登錄博客,很多朋友的留言沒有看見,這裏道歉!還有就是本人較少上QQ,可以郵件交流。


ceilometer-agent-central服務的初始化和啓動

    本篇博客將解析服務組件ceilometer-agent-central的初始化和啓動操作,ceilometer-agent-central服務組件運行在控制節點上,它主要通過調用相關模塊的REST API,通過訪問相關模塊的客戶端,從而實現主動收集相關模塊(Image,Volume,Objects,Network)的監控數據,需要定期Poll輪詢收集信息。

    來看方法/ceilometer/cli.py----def agent_central,這個方法即實現了ceilometer-agent-central服務的初始化和啓動操作。

def agent_central():
    """
    加載並啓動AgentManager服務;  
    Central Agent運行在控制節點上,它主要收集其它服務(Image,Volume,Objects,Network)的信息,實現邏輯和Compute Agent類似,但是是通過調用這些服務的REST API去獲取這些數據的。
    """
    service.prepare_service()
    os_service.launch(central_manager.AgentManager()).wait()

1 服務ceilometer-agent-central的初始化操作

服務ceilometer-agent-central的初始化操作主要實現了以下內容的操作:

(1)根據指定參數獲取命名空間ceilometer.poll.central,獲取與ceilometer.poll.central相匹配的所有插件,並加載;ceilometer.poll.central所指定的插件描述瞭如何獲取收集相關模塊(Image,Volume,Objects,Network)的監控數據。
(2)獲取管理員操作的上下文環境類的初始化對象;
(3)建立線程池,用於後續服務中若干操作的運行;

class AgentManager(agent.AgentManager)----def __init__

class AgentManager(agent.AgentManager):
    def __init__(self):
        super(AgentManager, self).__init__('central')
class AgentManager(os_service.Service)----def __init__

class AgentManager(os_service.Service):
    def __init__(self, namespace, default_discovery=[]):
        super(AgentManager, self).__init__()
        self.default_discovery = default_discovery
        
        """
        加載命名空間ceilometer.poll.central的所有插件:
        ceilometer.poll.central =
        ip.floating = ceilometer.network.floatingip:FloatingIPPollster
        image = ceilometer.image.glance:ImagePollster
        image.size = ceilometer.image.glance:ImageSizePollster
        storage.containers.objects = ceilometer.objectstore.swift:ContainersObjectsPollster
        storage.containers.objects.size = ceilometer.objectstore.swift:ContainersSizePollster
        storage.objects = ceilometer.objectstore.swift:ObjectsPollster
        storage.objects.size = ceilometer.objectstore.swift:ObjectsSizePollster
        storage.objects.containers = ceilometer.objectstore.swift:ObjectsContainersPollster
        energy = ceilometer.energy.kwapi:EnergyPollster
        power = ceilometer.energy.kwapi:PowerPollster
        switch.port = ceilometer.network.statistics.port:PortPollster
        switch.port.receive.packets = ceilometer.network.statistics.port:PortPollsterReceivePackets
        switch.port.transmit.packets = ceilometer.network.statistics.port:PortPollsterTransmitPackets
        switch.port.receive.bytes = ceilometer.network.statistics.port:PortPollsterReceiveBytes
        switch.port.transmit.bytes = ceilometer.network.statistics.port:PortPollsterTransmitBytes
        switch.port.receive.drops = ceilometer.network.statistics.port:PortPollsterReceiveDrops
        switch.port.transmit.drops = ceilometer.network.statistics.port:PortPollsterTransmitDrops
        switch.port.receive.errors = ceilometer.network.statistics.port:PortPollsterReceiveErrors
        switch.port.transmit.errors = ceilometer.network.statistics.port:PortPollsterTransmitErrors
        switch.port.receive.frame_error = ceilometer.network.statistics.port:PortPollsterReceiveFrameErrors
        switch.port.receive.overrun_error = ceilometer.network.statistics.port:PortPollsterReceiveOverrunErrors
        switch.port.receive.crc_error = ceilometer.network.statistics.port:PortPollsterReceiveCRCErrors
        switch.port.collision.count = ceilometer.network.statistics.port:PortPollsterCollisionCount
        switch.table = ceilometer.network.statistics.table:TablePollster
        switch.table.active.entries = ceilometer.network.statistics.table:TablePollsterActiveEntries
        switch.table.lookup.packets = ceilometer.network.statistics.table:TablePollsterLookupPackets
        switch.table.matched.packets = ceilometer.network.statistics.table:TablePollsterMatchedPackets
        switch = ceilometer.network.statistics.switch:SWPollster
        switch.flow = ceilometer.network.statistics.flow:FlowPollster
        switch.flow.bytes = ceilometer.network.statistics.flow:FlowPollsterBytes
        switch.flow.duration.nanoseconds = ceilometer.network.statistics.flow:FlowPollsterDurationNanoseconds
        switch.flow.duration.seconds = ceilometer.network.statistics.flow:FlowPollsterDurationSeconds
        switch.flow.packets = ceilometer.network.statistics.flow:FlowPollsterPackets
        hardware.cpu.load.1min = ceilometer.hardware.pollsters.cpu:CPULoad1MinPollster
        hardware.cpu.load.5min = ceilometer.hardware.pollsters.cpu:CPULoad5MinPollster
        hardware.cpu.load.15min = ceilometer.hardware.pollsters.cpu:CPULoad15MinPollster
        hardware.disk.size.total = ceilometer.hardware.pollsters.disk:DiskTotalPollster
        hardware.disk.size.used = ceilometer.hardware.pollsters.disk:DiskUsedPollster
        hardware.network.bandwidth.bytes = ceilometer.hardware.pollsters.net:BandwidthBytesPollster
        hardware.network.incoming.bytes = ceilometer.hardware.pollsters.net:IncomingBytesPollster
        hardware.network.outgoing.bytes = ceilometer.hardware.pollsters.net:OutgoingBytesPollster
        hardware.network.outgoing.errors = ceilometer.hardware.pollsters.net:OutgoingErrorsPollster
        hardware.memory.total = ceilometer.hardware.pollsters.memory:MemoryTotalPollster
        hardware.memory.used = ceilometer.hardware.pollsters.memory:MemoryUsedPollster
        """
        self.pollster_manager = self._extensions('poll', namespace)
        
        """
        加載ceilometer.discover所有插件:
        """
        self.discovery_manager = self._extensions('discover')
        self.context = context.RequestContext('admin', 'admin', is_admin=True)

class AgentManager(os_service.Service)----def _extensions

def _extensions(category, agent_ns=None):
    """
    根據指定參數獲取命名空間namespace,
    獲取與namespace相匹配的所有插件,並加載;
    """
    namespace = ('ceilometer.%s.%s' % (category, agent_ns) if agent_ns else 'ceilometer.%s' % category)
        
    """
    獲取與namespace相匹配的所有插件,並加載;
    """
    return extension.ExtensionManager(
        namespace=namespace,
        invoke_on_load=True,
    )

class Service(object)----def __init__

class Service(object):
    """Service object for binaries running on hosts."""
    def __init__(self, threads=1000):
        self.tg = threadgroup.ThreadGroup(threads)

        # signal that the service is done shutting itself down:
        self._done = event.Event()


2 服務ceilometer-agent-central的啓動操作

服務ceilometer-agent-central的啓動操作週期性地實現以下任務:
(1)遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
(2)針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列,其中實現採樣數據發佈的方式有三種,即RPC/UDP/FILE;

     其中,RPC將會發布相關消息到消息隊列,後續的collector組件服務將會監聽相應的消息隊列來獲取這些數據信息;UDP將會建立socket建立一個信息通道,實現發送相關消息數據,而後續的collector組件服務將會通過這個信息通道接收相關的消息數據;FILE將會直接保存相關消息數據到指定的日誌文件中。

class AgentManager(os_service.Service)----def start

class AgentManager(os_service.Service):
    def start(self):
        self.pipeline_manager = pipeline.setup_pipeline()
        for interval,task in self.setup_polling_tasks().iteritems():
            self.tg.add_timer(interval,
                              self.interval_task,
                              task=task)
class AgentManager(os_service.Service)----def interval_task
def interval_task(task):
        task.poll_and_publish()
class PollingTask(object)----def poll_and_publish

def poll_and_publish(self):
        """
        創建輪徇的任務;
        任務以一定時間間隔週期性地進行;
        
        遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
        針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列;
        """
        agent_resources = self.manager.discover()
        with self.publish_context as publisher:
            cache = {}    
            """
            遍歷任務(通道);
            獲取每個任務指定獲取的監控項的採樣數據;
            """
            for pollster in self.pollsters:
                key = pollster.name
                LOG.info(_("Polling pollster %s"), key)
                source_resources = list(self.resources[key].resources)
                try:
                    """
                    get_samples:獲取某一監控項的採樣數據;
                    注:各個get_samples方法的具體實現,都是通過相關客戶端訪問相關服務實現獲取相關的採樣數據;
                    class FloatingIPPollster(plugin.CentralPollster)----def get_samples(self, manager, cache)
                    class ImagePollster(_Base)----def get_samples(self, manager, cache)
                    class ImageSizePollster(_Base)----def get_samples(self, manager, cache)
                    class PowerPollster(_Base)----def get_samples(self, manager, cache)
                    class EnergyPollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsSizePollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsContainersPollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsPollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsPollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsSizePollster(_Base)----def get_samples(self, manager, cache)
                    class ObjectsContainersPollster(_Base)----def get_samples(self, manager, cache)
                    class ContainersObjectsPollster(_Base)----def get_samples(self, manager, cache)
                    class ContainersSizePollster(_Base)----def get_samples(self, manager, cache)
                    class HardwarePollster(plugin.CentralPollster)def get_samples(self, manager, cache, resources=[])
                    """
                    samples = list(pollster.obj.get_samples(
                        self.manager,
                        cache,
                        resources=source_resources or agent_resources,
                    ))
                    
                    """
                    實現發佈監控項採樣數據樣本;
                    """
                    publisher(samples)
                except Exception as err:
                    LOG.warning(_(
                        'Continue after error from %(name)s: %(error)s')
                        % ({'name': pollster.name, 'error': err}),
                        exc_info=True)

方法小結:
本方法週期性地執行以下操作:
(1)遍歷任務(通道),獲取每個任務指定獲取的監控項的採樣數據;
(2)針對每個監控項的採樣數據,實現發佈監控項採樣數據樣本到消息隊列;

class PublishContext----def __enter__

在上述代碼中,發佈監控項採樣樣本數據是由這個方法實現的,這個方法主要實現了以下內容:

實現發佈監控項採樣數據樣本,其中實現採樣數據發佈的方式有三種,即RPC/UDP/FILE;
(1).class FilePublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
實現發佈採樣數據到一個日誌文件;
(2).class RPCPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
實現通過RPC發佈採樣數據;
* 從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備;
* 將之前從採樣數據中提取的信息meters包裝成msg;
* 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;
* 實現發佈本地隊列local_queue中的所有數據信息到隊列metering中;
* 其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫);
(3).class UDPPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples)
通過UDP發佈採樣數據;

class PublishContext(object):
    def __enter__(self):
        """
        實現發佈監控項採樣數據樣本;
        publish_samples:
        1.class FilePublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
        實現發佈採樣數據到一個日誌文件;
        2.class RPCPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples);
        通過RPC發佈採樣數據;
        * 從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備;
        * 將之前從採樣數據中提取的信息meters包裝成msg;
        * 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;
        * 實現發佈本地隊列local_queue中的所有數據信息到隊列metering中;
        * 其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會
          執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫);
        3.class UDPPublisher(publisher.PublisherBase)----def publish_samples(self, context, samples)
        通過UDP發佈採樣數據;
        """
        def p(samples):
            for p in self.pipelines:
                p.publish_samples(self.context,
                                  samples)
        return p

2.1 實現發佈採樣數據到一個日誌文件

class FilePublisher(publisher.PublisherBase):
    def publish_samples(self, context, samples):
        if self.publisher_logger:
            for sample in samples:
                self.publisher_logger.info(sample.as_dict())
2.2 通過RPC發佈採樣數據(具體見代碼註釋)

class RPCPublisher(publisher.PublisherBase):
    def publish_samples(self, context, samples):
        """
        通過RPC發佈信息;
        1.從若干採樣數據信息samples中獲取提取數據形成信息格式meters,爲信息的發佈或存儲做準備;
        2.將之前從採樣數據中提取的信息meters包裝成msg;
        3.將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;
        4.實現發佈本地隊列local_queue中的所有數據信息到隊列metering中;
        5.其中,消息msg中封裝的'method'方法爲'record_metering_data',即當消息被消費時,將會
          執行方法record_metering_data,實現存儲到數據存儲系統中(數據庫);
        """

        # 從若干採樣數據信息中獲取提取數據形成信息格式,爲信息的發佈或存儲做準備;
        meters = [
            # meter_message_from_counter:
            # 爲一個監控採樣數據做好準備被髮布或存儲;
            # 從一個採樣數據信息中獲取提取信息形成msg;
            utils.meter_message_from_counter(
                sample,
                cfg.CONF.publisher.metering_secret)
            for sample in samples
        ]

        # cfg.CONF.publisher_rpc.metering_topic:metering messages所使用的主題,默認爲metering;
        topic = cfg.CONF.publisher_rpc.metering_topic
        
        # 將之前從採樣數據中提取的信息meters包裝成msg;
        msg = {
            'method': self.target,
            'version': '1.0',
            'args': {'data': meters},
        }
        
        # 將匹配的topic,msg添加到本地隊列local_queue中,topic默認爲metering;
        self.local_queue.append((context, topic, msg))

        if self.per_meter_topic:
            for meter_name, meter_list in itertools.groupby(
                    sorted(meters, key=operator.itemgetter('counter_name')),
                    operator.itemgetter('counter_name')):
                msg = {
                    'method': self.target,
                    'version': '1.0',
                    'args': {'data': list(meter_list)},
                }
                topic_name = topic + '.' + meter_name
                LOG.audit(_('Publishing %(m)d samples on %(n)s') % (
                          {'m': len(msg['args']['data']), 'n': topic_name}))
                self.local_queue.append((context, topic_name, msg))

        # 實現發佈本地隊列local_queue中的所有數據信息;
        self.flush()
    def flush(self):
        """
        實現發佈本地隊列中的所有數據信息;
        """        
        # 獲取本地隊列的數據信息;
        queue = self.local_queue
        self.local_queue = []
        
        # 實現循環發佈隊列queue中的信息數據;
        self.local_queue = self._process_queue(queue, self.policy) + \self.local_queue
        
        if self.policy == 'queue':
            self._check_queue_length()
    @staticmethod
    def _process_queue(queue, policy):
        """
        實現循環發佈隊列queue中的信息數據;
        """
        while queue:
            # 取出第一位的topic、msg等數據;
            context, topic, msg = queue[0]
            try:
                # 實現遠程發佈信息,不返回任何值;
                rpc.cast(context, topic, msg)
            except (SystemExit, rpc.common.RPCException):
                samples = sum([len(m['args']['data']) for n, n, m in queue])
                if policy == 'queue':
                    LOG.warn(_("Failed to publish %d samples, queue them"),samples)
                    return queue
                elif policy == 'drop':
                    LOG.warn(_("Failed to publish %d samples, dropping them"),samples)
                    return []
                # default, occur only if rabbit_max_retries > 0
                raise
            else:
                # 從隊列中刪除發佈後的信息;
                queue.pop(0)
        return []

2.3 通過UDP發佈採樣數據(具體見代碼註釋)

class UDPPublisher(publisher.PublisherBase):
    def publish_samples(self, context, samples):
        """
        通過UDP協議發送meter信息到服務器端,實現監控信息的發佈;
        """
        for sample in samples:
            """
            爲一個監控採樣數據做好準備被髮布或存儲;
            從一個採樣數據信息中獲取提取信息形成msg;
            """
            msg = utils.meter_message_from_counter(
                sample,
                cfg.CONF.publisher.metering_secret)
            host = self.host
            port = self.port
            LOG.debug(_("Publishing sample %(msg)s over UDP to "
                        "%(host)s:%(port)d") % {'msg': msg, 'host': host,'port': port})
            """
            通過UDP協議發送meter信息到服務器端,實現監控信息的發佈;
            """
            try:
                self.socket.sendto(msgpack.dumps(msg),(self.host, self.port))
            except Exception as e:
                LOG.warn(_("Unable to send sample over UDP"))
                LOG.exception(e)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章