~cloud-init-dev/cloud-init/trunk

« back to all changes in this revision

Viewing changes to cloudinit/sources/DataSourceAltCloud.py

  • Committer: Scott Moser
  • Date: 2016-08-10 15:06:15 UTC
  • Revision ID: smoser@ubuntu.com-20160810150615-ma2fv107w3suy1ma
README: Mention move of revision control to git.

cloud-init development has moved its revision control to git.
It is available at 
  https://code.launchpad.net/cloud-init

Clone with 
  git clone https://git.launchpad.net/cloud-init
or
  git clone git+ssh://git.launchpad.net/cloud-init

For more information see
  https://git.launchpad.net/cloud-init/tree/HACKING.rst

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# vi: ts=4 expandtab
2
 
#
3
 
#    Copyright (C) 2009-2010 Canonical Ltd.
4
 
#    Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P.
5
 
#    Copyright (C) 2012 Yahoo! Inc.
6
 
#
7
 
#    Author: Joe VLcek <JVLcek@RedHat.com>
8
 
#    Author: Juerg Haefliger <juerg.haefliger@hp.com>
9
 
#
10
 
#    This program is free software: you can redistribute it and/or modify
11
 
#    it under the terms of the GNU General Public License version 3, as
12
 
#    published by the Free Software Foundation.
13
 
#
14
 
#    This program is distributed in the hope that it will be useful,
15
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
#    GNU General Public License for more details.
18
 
#
19
 
#    You should have received a copy of the GNU General Public License
20
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
 
22
 
'''
23
 
This file contains code used to gather the user data passed to an
24
 
instance on RHEVm and vSphere.
25
 
'''
26
 
 
27
 
import errno
28
 
import os
29
 
import os.path
30
 
 
31
 
from cloudinit import log as logging
32
 
from cloudinit import sources
33
 
from cloudinit import util
34
 
 
35
 
from cloudinit.util import ProcessExecutionError
36
 
 
37
 
LOG = logging.getLogger(__name__)
38
 
 
39
 
# Needed file paths
40
 
CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info'
41
 
 
42
 
# Shell command lists
43
 
CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy']
44
 
CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--timeout=5']
45
 
 
46
 
META_DATA_NOT_SUPPORTED = {
47
 
    'block-device-mapping': {},
48
 
    'instance-id': 455,
49
 
    'local-hostname': 'localhost',
50
 
    'placement': {},
51
 
}
52
 
 
53
 
 
54
 
def read_user_data_callback(mount_dir):
55
 
    '''
56
 
    Description:
57
 
        This callback will be applied by util.mount_cb() on the mounted
58
 
        file.
59
 
 
60
 
        Deltacloud file name contains deltacloud. Those not using
61
 
        Deltacloud but instead instrumenting the injection, could
62
 
        drop deltacloud from the file name.
63
 
 
64
 
    Input:
65
 
        mount_dir - Mount directory
66
 
 
67
 
    Returns:
68
 
        User Data
69
 
 
70
 
    '''
71
 
 
72
 
    deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt'
73
 
    user_data_file = mount_dir + '/user-data.txt'
74
 
 
75
 
    # First try deltacloud_user_data_file. On failure try user_data_file.
76
 
    try:
77
 
        user_data = util.load_file(deltacloud_user_data_file).strip()
78
 
    except IOError:
79
 
        try:
80
 
            user_data = util.load_file(user_data_file).strip()
81
 
        except IOError:
82
 
            util.logexc(LOG, 'Failed accessing user data file.')
83
 
            return None
84
 
 
85
 
    return user_data
86
 
 
87
 
 
88
 
class DataSourceAltCloud(sources.DataSource):
89
 
    def __init__(self, sys_cfg, distro, paths):
90
 
        sources.DataSource.__init__(self, sys_cfg, distro, paths)
91
 
        self.seed = None
92
 
        self.supported_seed_starts = ("/", "file://")
93
 
 
94
 
    def __str__(self):
95
 
        root = sources.DataSource.__str__(self)
96
 
        return "%s [seed=%s]" % (root, self.seed)
97
 
 
98
 
    def get_cloud_type(self):
99
 
        '''
100
 
        Description:
101
 
            Get the type for the cloud back end this instance is running on
102
 
            by examining the string returned by reading the dmi data.
103
 
 
104
 
        Input:
105
 
            None
106
 
 
107
 
        Returns:
108
 
            One of the following strings:
109
 
            'RHEV', 'VSPHERE' or 'UNKNOWN'
110
 
 
111
 
        '''
112
 
 
113
 
        uname_arch = os.uname()[4]
114
 
        if uname_arch.startswith("arm") or uname_arch == "aarch64":
115
 
            # Disabling because dmi data is not available on ARM processors
116
 
            LOG.debug("Disabling AltCloud datasource on arm (LP: #1243287)")
117
 
            return 'UNKNOWN'
118
 
 
119
 
        system_name = util.read_dmi_data("system-product-name")
120
 
        if not system_name:
121
 
            return 'UNKNOWN'
122
 
 
123
 
        sys_name = system_name.upper()
124
 
 
125
 
        if sys_name.startswith('RHEV'):
126
 
            return 'RHEV'
127
 
 
128
 
        if sys_name.startswith('VMWARE'):
129
 
            return 'VSPHERE'
130
 
 
131
 
        return 'UNKNOWN'
132
 
 
133
 
    def get_data(self):
134
 
        '''
135
 
        Description:
136
 
            User Data is passed to the launching instance which
137
 
            is used to perform instance configuration.
138
 
 
139
 
            Cloud providers expose the user data differently.
140
 
            It is necessary to determine which cloud provider
141
 
            the current instance is running on to determine
142
 
            how to access the user data. Images built with
143
 
            image factory will contain a CLOUD_INFO_FILE which
144
 
            contains a string identifying the cloud provider.
145
 
 
146
 
            Images not built with Imagefactory will try to
147
 
            determine what the cloud provider is based on system
148
 
            information.
149
 
        '''
150
 
 
151
 
        LOG.debug('Invoked get_data()')
152
 
 
153
 
        if os.path.exists(CLOUD_INFO_FILE):
154
 
            try:
155
 
                cloud_type = util.load_file(CLOUD_INFO_FILE).strip().upper()
156
 
            except IOError:
157
 
                util.logexc(LOG, 'Unable to access cloud info file at %s.',
158
 
                            CLOUD_INFO_FILE)
159
 
                return False
160
 
        else:
161
 
            cloud_type = self.get_cloud_type()
162
 
 
163
 
        LOG.debug('cloud_type: ' + str(cloud_type))
164
 
 
165
 
        if 'RHEV' in cloud_type:
166
 
            if self.user_data_rhevm():
167
 
                return True
168
 
        elif 'VSPHERE' in cloud_type:
169
 
            if self.user_data_vsphere():
170
 
                return True
171
 
        else:
172
 
            # there was no recognized alternate cloud type
173
 
            # indicating this handler should not be used.
174
 
            return False
175
 
 
176
 
        # No user data found
177
 
        util.logexc(LOG, 'Failed accessing user data.')
178
 
        return False
179
 
 
180
 
    def user_data_rhevm(self):
181
 
        '''
182
 
        RHEVM specific userdata read
183
 
 
184
 
         If on RHEV-M the user data will be contained on the
185
 
         floppy device in file <user_data_file>
186
 
         To access it:
187
 
           modprobe floppy
188
 
 
189
 
           Leverage util.mount_cb to:
190
 
               mkdir <tmp mount dir>
191
 
               mount /dev/fd0 <tmp mount dir>
192
 
               The call back passed to util.mount_cb will do:
193
 
                   read <tmp mount dir>/<user_data_file>
194
 
        '''
195
 
 
196
 
        return_str = None
197
 
 
198
 
        # modprobe floppy
199
 
        try:
200
 
            cmd = CMD_PROBE_FLOPPY
201
 
            (cmd_out, _err) = util.subp(cmd)
202
 
            LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
203
 
        except ProcessExecutionError as _err:
204
 
            util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
205
 
                        _err.message)
206
 
            return False
207
 
        except OSError as _err:
208
 
            util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd), _err)
209
 
            return False
210
 
 
211
 
        floppy_dev = '/dev/fd0'
212
 
 
213
 
        # udevadm settle for floppy device
214
 
        try:
215
 
            cmd = CMD_UDEVADM_SETTLE
216
 
            cmd.append('--exit-if-exists=' + floppy_dev)
217
 
            (cmd_out, _err) = util.subp(cmd)
218
 
            LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
219
 
        except ProcessExecutionError as _err:
220
 
            util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
221
 
                        _err.message)
222
 
            return False
223
 
        except OSError as _err:
224
 
            util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
225
 
                        _err.message)
226
 
            return False
227
 
 
228
 
        try:
229
 
            return_str = util.mount_cb(floppy_dev, read_user_data_callback)
230
 
        except OSError as err:
231
 
            if err.errno != errno.ENOENT:
232
 
                raise
233
 
        except util.MountFailedError:
234
 
            util.logexc(LOG, "Failed to mount %s when looking for user data",
235
 
                        floppy_dev)
236
 
 
237
 
        self.userdata_raw = return_str
238
 
        self.metadata = META_DATA_NOT_SUPPORTED
239
 
 
240
 
        if return_str:
241
 
            return True
242
 
        else:
243
 
            return False
244
 
 
245
 
    def user_data_vsphere(self):
246
 
        '''
247
 
        vSphere specific userdata read
248
 
 
249
 
        If on vSphere the user data will be contained on the
250
 
        cdrom device in file <user_data_file>
251
 
        To access it:
252
 
           Leverage util.mount_cb to:
253
 
               mkdir <tmp mount dir>
254
 
               mount /dev/fd0 <tmp mount dir>
255
 
               The call back passed to util.mount_cb will do:
256
 
                   read <tmp mount dir>/<user_data_file>
257
 
        '''
258
 
 
259
 
        return_str = None
260
 
        cdrom_list = util.find_devs_with('LABEL=CDROM')
261
 
        for cdrom_dev in cdrom_list:
262
 
            try:
263
 
                return_str = util.mount_cb(cdrom_dev, read_user_data_callback)
264
 
                if return_str:
265
 
                    break
266
 
            except OSError as err:
267
 
                if err.errno != errno.ENOENT:
268
 
                    raise
269
 
            except util.MountFailedError:
270
 
                util.logexc(LOG, "Failed to mount %s when looking for user "
271
 
                            "data", cdrom_dev)
272
 
 
273
 
        self.userdata_raw = return_str
274
 
        self.metadata = META_DATA_NOT_SUPPORTED
275
 
 
276
 
        if return_str:
277
 
            return True
278
 
        else:
279
 
            return False
280
 
 
281
 
# Used to match classes to dependencies
282
 
# Source DataSourceAltCloud does not really depend on networking.
283
 
# In the future 'dsmode' like behavior can be added to offer user
284
 
# the ability to run before networking.
285
 
datasources = [
286
 
    (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
287
 
]
288
 
 
289
 
 
290
 
# Return a list of data sources that match this set of dependencies
291
 
def get_datasource_list(depends):
292
 
    return sources.list_from_depends(depends, datasources)