Completed
Push — master ( d2dcdf...3f6257 )
by Nicolas
01:19
created

ThreadAwsEc2Grabber.__init__()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2017 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Cloud plugin.
21
22
Supported Cloud API:
23
- AWS EC2 (class ThreadAwsEc2Grabber, see bellow)
24
"""
25
26
try:
27
    import requests
28
except ImportError:
29
    cloud_tag = False
30
else:
31
    cloud_tag = True
32
33
import threading
34
35
from glances.compat import iteritems, to_ascii
36
from glances.plugins.glances_plugin import GlancesPlugin
37
from glances.logger import logger
38
39
40
class Plugin(GlancesPlugin):
41
42
    """Glances' cloud plugin.
43
44
    The goal of this plugin is to retreive additional information
45
    concerning the datacenter where the host is connected.
46
47
    See https://github.com/nicolargo/glances/issues/1029
48
49
    stats is a dict
50
    """
51
52
    def __init__(self, args=None):
53
        """Init the plugin."""
54
        super(Plugin, self).__init__(args=args)
55
56
        # We want to display the stat in the curse interface
57
        self.display_curse = True
58
59
        # Init the stats
60
        self.reset()
61
62
        # Init thread to grab AWS EC2 stats asynchroniously
63
        self.aws_ec2 = ThreadAwsEc2Grabber()
64
65
        # Run the thread
66
        self.aws_ec2. start()
67
68
    def reset(self):
69
        """Reset/init the stats."""
70
        self.stats = {}
71
72
    def exit(self):
73
        """Overwrite the exit method to close threads"""
74
        self.aws_ec2.stop()
75
        # Call the father class
76
        super(Plugin, self).exit()
77
78
    @GlancesPlugin._check_decorator
79
    @GlancesPlugin._log_result_decorator
80
    def update(self):
81
        """Update the cloud stats.
82
83
        Return the stats (dict)
84
        """
85
        # Reset stats
86
        self.reset()
87
88
        # Requests lib is needed to get stats from the Cloud API
89
        if not cloud_tag:
90
            return self.stats
91
92
        # Update the stats
93
        if self.input_method == 'local':
94
            self.stats = self.aws_ec2.stats
95
            # self.stats = {'ami-id': 'ami-id',
96
            #                         'instance-id': 'instance-id',
97
            #                         'instance-type': 'instance-type',
98
            #                         'region': 'placement/availability-zone'}
99
100
        return self.stats
101
102
    def msg_curse(self, args=None):
103
        """Return the string to display in the curse interface."""
104
        # Init the return message
105
        ret = []
106
107
        if not self.stats or self.stats == {} or self.is_disable():
108
            return ret
109
110
        # Generate the output
111
        if 'ami-id' in self.stats and 'region' in self.stats:
112
            msg = 'AWS EC2'
113
            ret.append(self.curse_add_line(msg, "TITLE"))
114
            msg = ' {} instance {} ({})'.format(to_ascii(self.stats['instance-type']),
115
                                                to_ascii(self.stats['instance-id']),
116
                                                to_ascii(self.stats['region']))
117
            ret.append(self.curse_add_line(msg))
118
119
        # Return the message with decoration
120
        logger.info(ret)
121
        return ret
122
123
124
class ThreadAwsEc2Grabber(threading.Thread):
125
    """
126
    Specific thread to grab AWS EC2 stats.
127
128
    stats is a dict
129
    """
130
131
    # AWS EC2
132
    # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
133
    AWS_EC2_API_URL = 'http://169.254.169.254/latest/meta-data'
134
    AWS_EC2_API_METADATA = {'ami-id': 'ami-id',
135
                            'instance-id': 'instance-id',
136
                            'instance-type': 'instance-type',
137
                            'region': 'placement/availability-zone'}
138
139
    def __init__(self):
140
        """Init the class"""
141
        logger.debug("cloud plugin - Create thread for AWS EC2")
142
        super(ThreadAwsEc2Grabber, self).__init__()
143
        # Event needed to stop properly the thread
144
        self._stopper = threading.Event()
145
        # The class return the stats as a dict
146
        self._stats = {}
147
148
    def run(self):
149
        """Function called to grab stats.
150
        Infinite loop, should be stopped by calling the stop() method"""
151
152
        for k, v in iteritems(self.AWS_EC2_API_METADATA):
153
            r_url = '{}/{}'.format(self.AWS_EC2_API_URL, v)
154
            try:
155
                # Local request, a timeout of 3 seconds is OK
156
                r = requests.get(r_url, timeout=3)
157
            except requests.exceptions.ConnectTimeout:
158
                logger.debug('cloud plugin - Connection to {} timed out'.format(r_url))
159
                break
160
            except Exception as e:
161
                logger.debug('cloud plugin - Cannot connect to the AWS EC2 API {}: {}'.format(r_url, e))
162
                break
163
            else:
164
                if r.ok:
165
                    self._stats[k] = r.content
166
167
    @property
168
    def stats(self):
169
        """Stats getter"""
170
        return self._stats
171
172
    @stats.setter
173
    def stats(self, value):
174
        """Stats setter"""
175
        self._stats = value
176
177
    def stop(self, timeout=None):
178
        """Stop the thread"""
179
        logger.debug("cloud plugin - Close thread for AWS EC2")
180
        self._stopper.set()
181
182
    def stopped(self):
183
        """Return True is the thread is stopped"""
184
        return self._stopper.isSet()
185