~ubuntu-branches/debian/stretch/waagent/stretch

« back to all changes in this revision

Viewing changes to azurelinuxagent/common/protocol/metadata.py

  • Committer: Package Import Robot
  • Author(s): Bastian Blank
  • Date: 2016-08-24 16:48:22 UTC
  • mfrom: (1.2.5)
  • Revision ID: package-import@ubuntu.com-20160824164822-vdf8m5xy5gycm1cz
Tags: 2.1.6-1
New upstream version.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Microsoft Azure Linux Agent
 
2
#
 
3
# Copyright 2014 Microsoft Corporation
 
4
#
 
5
# Licensed under the Apache License, Version 2.0 (the "License");
 
6
# you may not use this file except in compliance with the License.
 
7
# You may obtain a copy of the License at
 
8
#
 
9
#     http://www.apache.org/licenses/LICENSE-2.0
 
10
#
 
11
# Unless required by applicable law or agreed to in writing, software
 
12
# distributed under the License is distributed on an "AS IS" BASIS,
 
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
# See the License for the specific language governing permissions and
 
15
# limitations under the License.
 
16
#
 
17
# Requires Python 2.4+ and Openssl 1.0+
 
18
 
 
19
import json
 
20
import shutil
 
21
import os
 
22
import time
 
23
from azurelinuxagent.common.exception import ProtocolError, HttpError
 
24
from azurelinuxagent.common.future import httpclient, ustr
 
25
import azurelinuxagent.common.conf as conf
 
26
import azurelinuxagent.common.logger as logger
 
27
import azurelinuxagent.common.utils.restutil as restutil
 
28
import azurelinuxagent.common.utils.textutil as textutil
 
29
import azurelinuxagent.common.utils.fileutil as fileutil
 
30
from azurelinuxagent.common.utils.cryptutil import CryptUtil
 
31
from azurelinuxagent.common.protocol.restapi import *
 
32
 
 
33
METADATA_ENDPOINT='169.254.169.254'
 
34
APIVERSION='2015-05-01-preview'
 
35
BASE_URI = "http://{0}/Microsoft.Compute/{1}?api-version={2}{3}"
 
36
 
 
37
TRANSPORT_PRV_FILE_NAME = "V2TransportPrivate.pem"
 
38
TRANSPORT_CERT_FILE_NAME = "V2TransportCert.pem"
 
39
 
 
40
#TODO remote workarround for azure stack 
 
41
MAX_PING = 30
 
42
RETRY_PING_INTERVAL = 10
 
43
 
 
44
def _add_content_type(headers):
 
45
    if headers is None:
 
46
        headers = {}
 
47
    headers["content-type"] = "application/json"
 
48
    return headers
 
49
 
 
50
class MetadataProtocol(Protocol):
 
51
 
 
52
    def __init__(self, apiversion=APIVERSION, endpoint=METADATA_ENDPOINT):
 
53
        self.apiversion = apiversion
 
54
        self.endpoint = endpoint
 
55
        self.identity_uri = BASE_URI.format(self.endpoint, "identity",
 
56
                                            self.apiversion, "&$expand=*")
 
57
        self.cert_uri = BASE_URI.format(self.endpoint, "certificates",
 
58
                                        self.apiversion, "&$expand=*")
 
59
        self.ext_uri = BASE_URI.format(self.endpoint, "extensionHandlers",
 
60
                                       self.apiversion, "&$expand=*")
 
61
        self.vmagent_uri = BASE_URI.format(self.endpoint, "vmAgentVersions",
 
62
                                           self.apiversion, "&$expand=*")
 
63
        self.provision_status_uri = BASE_URI.format(self.endpoint,
 
64
                                                    "provisioningStatus",
 
65
                                                    self.apiversion, "")
 
66
        self.vm_status_uri = BASE_URI.format(self.endpoint, "status/vmagent",
 
67
                                             self.apiversion, "")
 
68
        self.ext_status_uri = BASE_URI.format(self.endpoint, 
 
69
                                              "status/extensions/{0}",
 
70
                                              self.apiversion, "")
 
71
        self.event_uri = BASE_URI.format(self.endpoint, "status/telemetry",
 
72
                                         self.apiversion, "")
 
73
 
 
74
    def _get_data(self, url, headers=None):
 
75
        try:
 
76
            resp = restutil.http_get(url, headers=headers)
 
77
        except HttpError as e:
 
78
            raise ProtocolError(ustr(e))
 
79
 
 
80
        if resp.status != httpclient.OK:
 
81
            raise ProtocolError("{0} - GET: {1}".format(resp.status, url))
 
82
 
 
83
        data = resp.read()
 
84
        etag = resp.getheader('ETag')
 
85
        if data is None:
 
86
            return None
 
87
        data = json.loads(ustr(data, encoding="utf-8"))
 
88
        return data, etag
 
89
 
 
90
    def _put_data(self, url, data, headers=None):
 
91
        headers = _add_content_type(headers) 
 
92
        try:
 
93
            resp = restutil.http_put(url, json.dumps(data), headers=headers)
 
94
        except HttpError as e:
 
95
            raise ProtocolError(ustr(e))
 
96
        if resp.status != httpclient.OK:
 
97
            raise ProtocolError("{0} - PUT: {1}".format(resp.status, url))
 
98
 
 
99
    def _post_data(self, url, data, headers=None):
 
100
        headers = _add_content_type(headers) 
 
101
        try:
 
102
            resp = restutil.http_post(url, json.dumps(data), headers=headers)
 
103
        except HttpError as e:
 
104
            raise ProtocolError(ustr(e))
 
105
        if resp.status != httpclient.CREATED:
 
106
            raise ProtocolError("{0} - POST: {1}".format(resp.status, url))
 
107
    
 
108
    def _get_trans_cert(self):
 
109
        trans_crt_file = os.path.join(conf.get_lib_dir(), 
 
110
                                      TRANSPORT_CERT_FILE_NAME)
 
111
        if not os.path.isfile(trans_crt_file):
 
112
            raise ProtocolError("{0} is missing.".format(trans_crt_file))
 
113
        content = fileutil.read_file(trans_crt_file)
 
114
        return textutil.get_bytes_from_pem(content)
 
115
 
 
116
    def detect(self):
 
117
        self.get_vminfo()
 
118
        trans_prv_file = os.path.join(conf.get_lib_dir(), 
 
119
                                      TRANSPORT_PRV_FILE_NAME)
 
120
        trans_cert_file = os.path.join(conf.get_lib_dir(), 
 
121
                                       TRANSPORT_CERT_FILE_NAME)
 
122
        cryptutil = CryptUtil(conf.get_openssl_cmd())
 
123
        cryptutil.gen_transport_cert(trans_prv_file, trans_cert_file)
 
124
 
 
125
        #"Install" the cert and private key to /var/lib/waagent
 
126
        thumbprint = cryptutil.get_thumbprint_from_crt(trans_cert_file)
 
127
        prv_file = os.path.join(conf.get_lib_dir(), 
 
128
                                "{0}.prv".format(thumbprint))
 
129
        crt_file = os.path.join(conf.get_lib_dir(), 
 
130
                                "{0}.crt".format(thumbprint))
 
131
        shutil.copyfile(trans_prv_file, prv_file)
 
132
        shutil.copyfile(trans_cert_file, crt_file)
 
133
 
 
134
 
 
135
    def get_vminfo(self):
 
136
        vminfo = VMInfo()
 
137
        data, etag = self._get_data(self.identity_uri)
 
138
        set_properties("vminfo", vminfo, data)
 
139
        return vminfo
 
140
 
 
141
    def get_certs(self):
 
142
        #TODO download and save certs
 
143
        return CertList()
 
144
 
 
145
    def get_vmagent_manifests(self, last_etag=None):
 
146
        manifests = VMAgentManifestList()
 
147
        data, etag = self._get_data(self.vmagent_uri)
 
148
        if last_etag == None or last_etag < etag:
 
149
            set_properties("vmAgentManifests", manifests.vmAgentManifests, data)
 
150
        return manifests, etag
 
151
 
 
152
    def get_vmagent_pkgs(self, vmagent_manifest):
 
153
        #Agent package is the same with extension handler
 
154
        vmagent_pkgs = ExtHandlerPackageList()
 
155
        data = None
 
156
        for manifest_uri in vmagent_manifest.versionsManifestUris:
 
157
            try:
 
158
                data = self._get_data(manifest_uri.uri)
 
159
                break
 
160
            except ProtocolError as e:
 
161
                logger.warn("Failed to get vmagent versions: {0}", e)
 
162
                logger.info("Retry getting vmagent versions")
 
163
        if data is None:
 
164
            raise ProtocolError(("Failed to get versions for vm agent: {0}"
 
165
                                 "").format(vmagent_manifest.family))
 
166
        set_properties("vmAgentVersions", vmagent_pkgs, data)
 
167
        # TODO: What etag should this return?
 
168
        return vmagent_pkgs
 
169
 
 
170
    def get_ext_handlers(self, last_etag=None):
 
171
        headers = {
 
172
            "x-ms-vmagent-public-x509-cert": self._get_trans_cert()
 
173
        }
 
174
        ext_list = ExtHandlerList()
 
175
        data, etag = self._get_data(self.ext_uri, headers=headers)
 
176
        if last_etag == None or last_etag < etag:
 
177
            set_properties("extensionHandlers", ext_list.extHandlers, data)
 
178
        return ext_list, etag
 
179
 
 
180
    def get_ext_handler_pkgs(self, ext_handler):
 
181
        ext_handler_pkgs = ExtHandlerPackageList()
 
182
        data = None
 
183
        for version_uri in ext_handler.versionUris:
 
184
            try:
 
185
                data, etag = self._get_data(version_uri.uri)
 
186
                break
 
187
            except ProtocolError as e:
 
188
                logger.warn("Failed to get version uris: {0}", e)
 
189
                logger.info("Retry getting version uris")
 
190
        set_properties("extensionPackages", ext_handler_pkgs, data)
 
191
        return ext_handler_pkgs
 
192
 
 
193
    def report_provision_status(self, provision_status):
 
194
        validate_param('provisionStatus', provision_status, ProvisionStatus)
 
195
        data = get_properties(provision_status)
 
196
        self._put_data(self.provision_status_uri, data)
 
197
 
 
198
    def report_vm_status(self, vm_status):
 
199
        validate_param('vmStatus', vm_status, VMStatus)
 
200
        data = get_properties(vm_status)
 
201
        #TODO code field is not implemented for metadata protocol yet. Remove it
 
202
        handler_statuses = data['vmAgent']['extensionHandlers']
 
203
        for handler_status in handler_statuses:
 
204
            try:
 
205
                handler_status.pop('code', None)
 
206
            except KeyError:
 
207
                pass
 
208
 
 
209
        self._put_data(self.vm_status_uri, data)
 
210
 
 
211
    def report_ext_status(self, ext_handler_name, ext_name, ext_status):
 
212
        validate_param('extensionStatus', ext_status, ExtensionStatus)
 
213
        data = get_properties(ext_status)
 
214
        uri = self.ext_status_uri.format(ext_name)
 
215
        self._put_data(uri, data)
 
216
 
 
217
    def report_event(self, events):
 
218
        #TODO disable telemetry for azure stack test
 
219
        #validate_param('events', events, TelemetryEventList)
 
220
        #data = get_properties(events)
 
221
        #self._post_data(self.event_uri, data)
 
222
        pass
 
223