~chad.smith/landscape-client/ha-manager-skeleton

« back to all changes in this revision

Viewing changes to landscape/monitor/swiftdeviceinfo.py

Add separate swift-device-info plugin as a monitor plugin which will read any available swift ring files and pull swift device information from curl to the discovered local swift-recon sevice at http://<local_ip>:<swift_service_port>/recon/diskusage. 

Once swift devices are discovered, send this info back to the landscape-server which will process the new swift-device-info messages.

The provider is disabled and logs entered if:
- swift config files don't exist on the server
- no local swift recon service is advertised or running [f=1100817] [r=ack,tribaal,jseutter]

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import logging
 
2
import time
 
3
import os
 
4
import json
 
5
 
 
6
from landscape.lib.fetch import fetch, HTTPCodeError, PyCurlError, FetchError
 
7
from landscape.lib.monitor import CoverageMonitor
 
8
from landscape.lib.network import get_active_device_info
 
9
from landscape.monitor.plugin import MonitorPlugin
 
10
 
 
11
 
 
12
class SwiftDeviceInfo(MonitorPlugin):
 
13
 
 
14
    persist_name = "swift-device-info"
 
15
 
 
16
    def __init__(self, interval=300, monitor_interval=60 * 60,
 
17
                 create_time=time.time,
 
18
                 swift_config="/etc/swift/object-server.conf",
 
19
                 swift_ring="/etc/swift/object.ring.gz"):
 
20
        self.run_interval = interval
 
21
        self._monitor_interval = monitor_interval
 
22
        self._create_time = create_time
 
23
        self._fetch = fetch
 
24
        self._get_network_devices = get_active_device_info
 
25
        self._swift_config = swift_config  # If exists, we are a swift node
 
26
        self._swift_ring = swift_ring      # To discover swift recon port
 
27
        self._swift_recon_url = None
 
28
        self._create_time = create_time
 
29
        self._swift_device_info = []
 
30
        self._swift_device_info_to_persist = []
 
31
        self.enabled = True
 
32
 
 
33
    def register(self, registry):
 
34
        super(SwiftDeviceInfo, self).register(registry)
 
35
        self._monitor = CoverageMonitor(self.run_interval, 0.8,
 
36
                                        "swift device info snapshot",
 
37
                                        create_time=self._create_time)
 
38
        self.registry.reactor.call_every(self._monitor_interval,
 
39
                                         self._monitor.log)
 
40
        self.registry.reactor.call_on("stop", self._monitor.log, priority=2000)
 
41
        self.call_on_accepted("swift-device-info", self.send_messages, True)
 
42
 
 
43
    def create_swift_device_info_message(self):
 
44
        if self._swift_device_info:
 
45
            message = {"type": "swift-device-info",
 
46
                       "swift-device-info": self._swift_device_info}
 
47
            self._swift_device_info_to_persist = self._swift_device_info[:]
 
48
            self._swift_device_info = []
 
49
            return message
 
50
        return None
 
51
 
 
52
    def send_messages(self, urgent=False):
 
53
        message = self.create_swift_device_info_message()
 
54
        if message:
 
55
            logging.info("Queueing message with updated swift device info.")
 
56
            d = self.registry.broker.send_message(message, urgent=urgent)
 
57
            d.addCallback(lambda x: self.persist_swift_info())
 
58
 
 
59
    def exchange(self):
 
60
        self.registry.broker.call_if_accepted("swift-device-info",
 
61
                                              self.send_messages)
 
62
 
 
63
    def persist_swift_info(self):
 
64
        for swift_device_info in self._swift_device_info_to_persist:
 
65
            device_name = swift_device_info["device"]
 
66
            key = (self.persist_name, device_name)
 
67
            self._persist.set(key, swift_device_info)
 
68
        self._swift_device_info_to_persist = None
 
69
        # This forces the registry to write the persistent store to disk
 
70
        # This means that the persistent data reflects the state of the
 
71
        # messages sent.
 
72
        self.registry.flush()
 
73
 
 
74
    def run(self):
 
75
        if not self.enabled:
 
76
            return
 
77
        self._monitor.ping()
 
78
 
 
79
        current_swift_devices = self._get_swift_devices()
 
80
        current_device_names = []
 
81
        for swift_info in current_swift_devices:
 
82
            device_name = swift_info["device"]
 
83
            current_device_names.append(device_name)
 
84
            key = (self.persist_name, device_name)
 
85
            prev_swift_info = self._persist.get(key)
 
86
            if not prev_swift_info or prev_swift_info != swift_info:
 
87
                if swift_info not in self._swift_device_info:
 
88
                    self._swift_device_info.append(swift_info)
 
89
 
 
90
        # Get all persisted devices and remove those that no longer exist
 
91
        persisted_devices = self._persist.get(self.persist_name)
 
92
        if persisted_devices:
 
93
            for device_name in persisted_devices.keys():
 
94
                if device_name not in current_device_names:
 
95
                    self._persist.remove((self.persist_name, device_name))
 
96
 
 
97
    def _get_swift_devices(self):
 
98
        config_file = self._swift_config
 
99
        # Check if a swift storage config file is available. No need to run
 
100
        # if we know that we're not on a swift monitor node anyway.
 
101
        if not os.path.exists(config_file):
 
102
            # There is no config file - it's not a swift storage machine.
 
103
            self.enabled = False
 
104
            logging.info(
 
105
                    "This does not appear to be a swift storage server. '%s' "
 
106
                    "plugin has been disabled." % self.persist_name)
 
107
            return []
 
108
 
 
109
        # Extract the swift service URL from the ringfile and cache it.
 
110
        if self._swift_recon_url is None:
 
111
            ring = self._get_ring()
 
112
            if ring is None:
 
113
                return []
 
114
 
 
115
            network_devices = self._get_network_devices()
 
116
            local_ips = [device["ip_address"] for device in network_devices]
 
117
 
 
118
            # Grab first swift service with an IP on this host
 
119
            for dev in ring.devs:
 
120
                if dev and dev["ip"] in local_ips:
 
121
                    self._swift_recon_url = "http://%s:%d/recon/diskusage" % (
 
122
                        dev['ip'], dev['port'])
 
123
                    break
 
124
 
 
125
            if self._swift_recon_url is None:
 
126
                self.enabled = False
 
127
                logging.error(
 
128
                    "Local swift service not found. '%s' plugin has "
 
129
                    "been disabled." % self.persist_name)
 
130
                return []
 
131
 
 
132
        recon_disk_info = self._get_swift_disk_usage()
 
133
        # We don't care about avail and free figures because we track
 
134
        # free_space for mounted devices in free-space messages
 
135
        return [{"device": "/dev/%s" % device["device"],
 
136
                 "mounted":  device["mounted"]} for device in recon_disk_info]
 
137
 
 
138
    def _get_swift_disk_usage(self):
 
139
        """
 
140
        Query the swift storage usage data by parsing the curled recon data
 
141
        from http://localhost:<_swift_service_port>/recon/diskusage.
 
142
        Lots of recon data for the picking described at:
 
143
        http://docs.openstack.org/developer/swift/admin_guide.html
 
144
        """
 
145
        error_message = None
 
146
        try:
 
147
            content = self._fetch(self._swift_recon_url)
 
148
        except HTTPCodeError, error:
 
149
            error_message = (
 
150
                "Swift service is running without swift-recon enabled.")
 
151
        except (FetchError, PyCurlError), error:
 
152
            error_message = (
 
153
                "Swift service not available at %s. %s." %
 
154
                (self._swift_recon_url, str(error)))
 
155
        if error_message is not None:
 
156
            self.enabled = False
 
157
            logging.error("%s '%s' plugin has been disabled." % (
 
158
                error_message, self.persist_name))
 
159
            return None
 
160
 
 
161
        if not content:
 
162
            return None
 
163
 
 
164
        swift_disk_usages = json.loads(content)  # list of device dicts
 
165
        return swift_disk_usages
 
166
 
 
167
    def _get_ring(self):
 
168
        """Return ring-file object from self._swift_ring location"""
 
169
        if not os.path.exists(self._swift_ring):
 
170
            logging.warning(
 
171
                "Swift ring files are not available yet.")
 
172
            return None
 
173
        try:
 
174
            from swift.common.ring import Ring
 
175
        except ImportError:
 
176
            self.enabled = False
 
177
            logging.error(
 
178
                "Swift python common libraries not found. '%s' plugin has "
 
179
                "been disabled." % self.persist_name)
 
180
            return None
 
181
        return Ring(self._swift_ring)