1
# Microsoft Azure Linux Agent
3
# Copyright 2014 Microsoft Corporation
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
9
# http://www.apache.org/licenses/LICENSE-2.0
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.
17
# Requires Python 2.4+ and Openssl 1.0+
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 *
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}"
37
TRANSPORT_PRV_FILE_NAME = "V2TransportPrivate.pem"
38
TRANSPORT_CERT_FILE_NAME = "V2TransportCert.pem"
40
#TODO remote workarround for azure stack
42
RETRY_PING_INTERVAL = 10
44
def _add_content_type(headers):
47
headers["content-type"] = "application/json"
50
class MetadataProtocol(Protocol):
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,
66
self.vm_status_uri = BASE_URI.format(self.endpoint, "status/vmagent",
68
self.ext_status_uri = BASE_URI.format(self.endpoint,
69
"status/extensions/{0}",
71
self.event_uri = BASE_URI.format(self.endpoint, "status/telemetry",
74
def _get_data(self, url, headers=None):
76
resp = restutil.http_get(url, headers=headers)
77
except HttpError as e:
78
raise ProtocolError(ustr(e))
80
if resp.status != httpclient.OK:
81
raise ProtocolError("{0} - GET: {1}".format(resp.status, url))
84
etag = resp.getheader('ETag')
87
data = json.loads(ustr(data, encoding="utf-8"))
90
def _put_data(self, url, data, headers=None):
91
headers = _add_content_type(headers)
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))
99
def _post_data(self, url, data, headers=None):
100
headers = _add_content_type(headers)
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))
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)
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)
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)
135
def get_vminfo(self):
137
data, etag = self._get_data(self.identity_uri)
138
set_properties("vminfo", vminfo, data)
142
#TODO download and save certs
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
152
def get_vmagent_pkgs(self, vmagent_manifest):
153
#Agent package is the same with extension handler
154
vmagent_pkgs = ExtHandlerPackageList()
156
for manifest_uri in vmagent_manifest.versionsManifestUris:
158
data = self._get_data(manifest_uri.uri)
160
except ProtocolError as e:
161
logger.warn("Failed to get vmagent versions: {0}", e)
162
logger.info("Retry getting vmagent versions")
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?
170
def get_ext_handlers(self, last_etag=None):
172
"x-ms-vmagent-public-x509-cert": self._get_trans_cert()
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
180
def get_ext_handler_pkgs(self, ext_handler):
181
ext_handler_pkgs = ExtHandlerPackageList()
183
for version_uri in ext_handler.versionUris:
185
data, etag = self._get_data(version_uri.uri)
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
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)
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:
205
handler_status.pop('code', None)
209
self._put_data(self.vm_status_uri, data)
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)
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)