Merge lp:~darkmuggle-deactivatedaccount/ubuntu/quantal/walinuxagent/lp1077148 into lp:ubuntu/quantal/walinuxagent

Proposed by Ben Howard
Status: Merged
Merge reported by: James Page
Merged at revision: not available
Proposed branch: lp:~darkmuggle-deactivatedaccount/ubuntu/quantal/walinuxagent/lp1077148
Merge into: lp:ubuntu/quantal/walinuxagent
Diff against target: 8316 lines (+2824/-5015)
22 files modified
.pc/.quilt_patches (+0/-1)
.pc/.quilt_series (+0/-1)
.pc/.version (+0/-1)
.pc/000_ubuntu_init_resolvconf.patch/waagent (+0/-2335)
.pc/000_use_package_upstart.patch/waagent (+2473/-0)
.pc/001_ubuntu_agent_startup.patch/waagent (+0/-2395)
.pc/applied-patches (+0/-2)
Changelog (+25/-0)
debian/changelog (+60/-0)
debian/control (+10/-0)
debian/patches/000_ubuntu_init_resolvconf.patch (+0/-153)
debian/patches/000_use_package_upstart.patch (+16/-0)
debian/patches/001_ubuntu_agent_startup.patch (+0/-22)
debian/patches/series (+0/-2)
debian/postinst (+7/-2)
debian/preinst (+16/-0)
debian/prerm (+2/-3)
debian/rules (+3/-4)
debian/upstart (+24/-0)
debian/walinuxagent-data-saver.lintian-overrides (+10/-0)
debian/walinuxagent-data-saver.preinst (+16/-0)
waagent (+162/-94)
To merge this branch: bzr merge lp:~darkmuggle-deactivatedaccount/ubuntu/quantal/walinuxagent/lp1077148
Reviewer Review Type Date Requested Status
James Page Needs Fixing
Review via email: mp+139716@code.launchpad.net

Description of the change

This package fixes the following issues:
    - Added - load ata_piix.ko module loaded if needed for CDROM device support
    - Additional logging for DoDhcpWork()
    - Update sock.recv timeout from 30 to 10 seconds
    - Fix: Linux waagent deprovision, user is not deleted properly
    - Fix: Make LBProbeResponder construction more robust
    - Fix: Agent fails to provision user with public/private key pairs
    - Fix: DHCP broadcast response not received
    - Fix: Linux agent fails to delete root user password
    - Fix: Linux agent should report error messages to Fabric when
           passed an invalid hostname.

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

walinuxagent (1.2-0ubuntu1~12.10.1ubuntu1) quantal-proposed; urgency=high

->

walinuxagent (1.2-0ubuntu1~12.10.1) quantal-proposed; urgency=high

review: Needs Fixing
8. By Ben Howard

Correction of version number

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc'
2=== removed directory '.pc'
3=== added file '.pc/.quilt_patches'
4--- .pc/.quilt_patches 1970-01-01 00:00:00 +0000
5+++ .pc/.quilt_patches 2012-12-13 16:29:21 +0000
6@@ -0,0 +1,1 @@
7+debian/patches
8
9=== removed file '.pc/.quilt_patches'
10--- .pc/.quilt_patches 2012-06-22 09:10:22 +0000
11+++ .pc/.quilt_patches 1970-01-01 00:00:00 +0000
12@@ -1,1 +0,0 @@
13-debian/patches
14
15=== added file '.pc/.quilt_series'
16--- .pc/.quilt_series 1970-01-01 00:00:00 +0000
17+++ .pc/.quilt_series 2012-12-13 16:29:21 +0000
18@@ -0,0 +1,1 @@
19+series
20
21=== removed file '.pc/.quilt_series'
22--- .pc/.quilt_series 2012-06-22 09:10:22 +0000
23+++ .pc/.quilt_series 1970-01-01 00:00:00 +0000
24@@ -1,1 +0,0 @@
25-series
26
27=== added file '.pc/.version'
28--- .pc/.version 1970-01-01 00:00:00 +0000
29+++ .pc/.version 2012-12-13 16:29:21 +0000
30@@ -0,0 +1,1 @@
31+2
32
33=== removed file '.pc/.version'
34--- .pc/.version 2012-06-22 09:10:22 +0000
35+++ .pc/.version 1970-01-01 00:00:00 +0000
36@@ -1,1 +0,0 @@
37-2
38
39=== removed directory '.pc/000_ubuntu_init_resolvconf.patch'
40=== removed file '.pc/000_ubuntu_init_resolvconf.patch/waagent'
41--- .pc/000_ubuntu_init_resolvconf.patch/waagent 2012-06-22 09:10:22 +0000
42+++ .pc/000_ubuntu_init_resolvconf.patch/waagent 1970-01-01 00:00:00 +0000
43@@ -1,2335 +0,0 @@
44-#!/usr/bin/python
45-#
46-# Windows Azure Linux Agent
47-#
48-# Copyright 2012 Microsoft Corporation
49-#
50-# Licensed under the Apache License, Version 2.0 (the "License");
51-# you may not use this file except in compliance with the License.
52-# You may obtain a copy of the License at
53-#
54-# http://www.apache.org/licenses/LICENSE-2.0
55-#
56-# Unless required by applicable law or agreed to in writing, software
57-# distributed under the License is distributed on an "AS IS" BASIS,
58-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
59-# See the License for the specific language governing permissions and
60-# limitations under the License.
61-#
62-# Requires Python 2.4+ and Openssl 1.0+
63-#
64-# Implements parts of RFC 2131, 1541, 1497 and
65-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
66-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
67-#
68-
69-import array
70-import base64
71-import httplib
72-import os
73-import os.path
74-import platform
75-import pwd
76-import re
77-import shutil
78-import socket
79-import SocketServer
80-import struct
81-import sys
82-import tempfile
83-import textwrap
84-import threading
85-import time
86-import traceback
87-import xml.dom.minidom
88-
89-GuestAgentName = "WALinuxAgent"
90-GuestAgentLongName = "Windows Azure Linux Agent"
91-GuestAgentVersion = "rd_wala.120504-1323"
92-ProtocolVersion = "2011-12-31"
93-
94-Config = None
95-LinuxDistro = "UNKNOWN"
96-Verbose = False
97-WaAgent = None
98-DiskActivated = False
99-Openssl = "openssl"
100-
101-PossibleEthernetInterfaces = ["seth0", "seth1", "eth0", "eth1"]
102-RulesFiles = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
103- "/etc/udev/rules.d/70-persistent-net.rules" ]
104-VarLibDhcpDirectories = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"]
105-EtcDhcpClientConfFiles = ["/etc/dhcp/dhclient.conf", "/etc/dhcp3/dhclient.conf"]
106-LibDir = "/var/lib/waagent"
107-
108-# This lets us index into a string or an array of integers transparently.
109-def Ord(a):
110- if type(a) == type("a"):
111- a = ord(a)
112- return a
113-
114-def IsWindows():
115- return (platform.uname()[0] == "Windows")
116-
117-def IsLinux():
118- return (platform.uname()[0] == "Linux")
119-
120-def DetectLinuxDistro():
121- global LinuxDistro
122- if os.path.isfile("/etc/redhat-release"):
123- LinuxDistro = "RedHat"
124- return True
125- if os.path.isfile("/etc/lsb-release") and "Ubuntu" in GetFileContents("/etc/lsb-release"):
126- LinuxDistro = "Ubuntu"
127- return True
128- if os.path.isfile("/etc/debian_version"):
129- LinuxDistro = "Debian"
130- return True
131- if os.path.isfile("/etc/SuSE-release"):
132- LinuxDistro = "Suse"
133- return True
134- return False
135-
136-def IsRedHat():
137- return "RedHat" in LinuxDistro
138-
139-def IsUbuntu():
140- return "Ubuntu" in LinuxDistro
141-
142-def IsDebian():
143- return IsUbuntu() or "Debian" in LinuxDistro
144-
145-def IsSuse():
146- return "Suse" in LinuxDistro
147-
148-def UsesRpm():
149- return IsRedHat() or IsSuse()
150-
151-def UsesDpkg():
152- return IsDebian()
153-
154-def GetLastPathElement(path):
155- return path.rsplit('/', 1)[1]
156-
157-def GetFileContents(filepath):
158- file = None
159- try:
160- file = open(filepath)
161- except:
162- return None
163- if file == None:
164- return None
165- try:
166- return file.read()
167- finally:
168- file.close()
169-
170-def SetFileContents(filepath, contents):
171- file = open(filepath, "w")
172- try:
173- file.write(contents)
174- finally:
175- file.close()
176-
177-def AppendFileContents(filepath, contents):
178- file = open(filepath, "a")
179- try:
180- file.write(contents)
181- finally:
182- file.close()
183-
184-def ReplaceFileContentsAtomic(filepath, contents):
185- handle, temp = tempfile.mkstemp(dir = os.path.dirname(filepath))
186- try:
187- os.write(handle, contents)
188- finally:
189- os.close(handle)
190- try:
191- os.rename(temp, filepath)
192- return
193- except:
194- pass
195- os.remove(filepath)
196- os.rename(temp, filepath)
197-
198-def GetLineStartingWith(prefix, filepath):
199- for line in GetFileContents(filepath).split('\n'):
200- if line.startswith(prefix):
201- return line
202- return None
203-
204-def Run(a):
205- LogIfVerbose(a)
206- return os.system(a)
207-
208-def GetNodeTextData(a):
209- for b in a.childNodes:
210- if b.nodeType == b.TEXT_NODE:
211- return b.data
212-
213-def GetHome():
214- home = None
215- try:
216- home = GetLineStartingWith("HOME", "/etc/default/useradd").split('=')[1].strip()
217- except:
218- pass
219- if (home == None) or (home.startswith("/") == False):
220- home = "/home"
221- return home
222-
223-def ChangeOwner(filepath, user):
224- p = None
225- try:
226- p = pwd.getpwnam(user)
227- except:
228- pass
229- if p != None:
230- os.chown(filepath, p[2], p[3])
231-
232-def CreateDir(dirpath, user, mode):
233- try:
234- os.makedirs(dirpath, mode)
235- except:
236- pass
237- ChangeOwner(dirpath, user)
238-
239-def CreateAccount(user, password, expiration, thumbprint):
240- if IsWindows():
241- Log("Skipping CreateAccount on Windows")
242- return None
243- userentry = None
244- try:
245- userentry = pwd.getpwnam(user)
246- except:
247- pass
248- uidmin = None
249- try:
250- uidmin = int(GetLineStartingWith("UID_MIN", "/etc/login.defs").split()[1])
251- except:
252- pass
253- if uidmin == None:
254- uidmin = 100
255- if userentry != None and userentry[2] < uidmin:
256- Error("CreateAccount: " + user + " is a system user. Will not set password.")
257- return "Failed to set password for system user: " + user + " (0x06)."
258- if userentry == None:
259- command = "useradd -m " + user
260- if expiration != None:
261- command += " -e " + expiration.split('.')[0]
262- if Run(command):
263- Error("Failed to create user account: " + user)
264- return "Failed to create user account: " + user + " (0x07)."
265- else:
266- Log("CreateAccount: " + user + " already exists. Will update password.")
267- if password != None:
268- os.popen("chpasswd", "w").write(user + ":" + password + "\n")
269- try:
270- if password == None:
271- SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n")
272- else:
273- SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) ALL\n")
274- os.chmod("/etc/sudoers.d/waagent", 0440)
275- except:
276- Error("CreateAccount: Failed to configure sudo access for user.")
277- return "Failed to configure sudo privileges (0x08)."
278- home = GetHome()
279- if thumbprint != None:
280- dir = home + "/" + user + "/.ssh"
281- CreateDir(dir, user, 0700)
282- pub = dir + "/id_rsa.pub"
283- prv = dir + "/id_rsa"
284- Run("ssh-keygen -y -f " + thumbprint + ".prv > " + pub)
285- SetFileContents(prv, GetFileContents(thumbprint + ".prv"))
286- for f in [pub, prv]:
287- os.chmod(f, 0600)
288- ChangeOwner(f, user)
289- SetFileContents(dir + "/authorized_keys", GetFileContents(pub))
290- ChangeOwner(dir + "/authorized_keys", user)
291- Log("Created user account: " + user)
292- return None
293-
294-def DeleteAccount(user):
295- if IsWindows():
296- Log("Skipping DeleteAccount on Windows")
297- return
298- userentry = None
299- try:
300- userentry = pwd.getpwnam(user)
301- except:
302- pass
303- if userentry == None:
304- Error("DeleteAccount: " + user + " not found.")
305- return
306- uidmin = None
307- try:
308- uidmin = int(GetLineStartingWith("UID_MIN", "/etc/login.defs").split()[1])
309- except:
310- pass
311- if uidmin == None:
312- uidmin = 100
313- if userentry[2] < uidmin:
314- Error("DeleteAccount: " + user + " is a system user. Will not delete account.")
315- return
316- Run("userdel -f -r " + user)
317- try:
318- os.remove("/etc/sudoers.d/waagent")
319- except:
320- pass
321- return
322-
323-def ReloadSshd():
324- name = None
325- if IsRedHat() or IsSuse():
326- name = "sshd"
327- if IsDebian():
328- name = "ssh"
329- if name == None:
330- return
331- if not Run("service " + name + " status | grep running"):
332- Run("service " + name + " reload")
333-
334-def IsInRangeInclusive(a, low, high):
335- return (a >= low and a <= high)
336-
337-def IsPrintable(ch):
338- return IsInRangeInclusive(ch, Ord('A'), Ord('Z')) or IsInRangeInclusive(ch, Ord('a'), Ord('z')) or IsInRangeInclusive(ch, Ord('0'), Ord('9'))
339-
340-def HexDump(buffer, size):
341- if size < 0:
342- size = len(buffer)
343- result = ""
344- for i in range(0, size):
345- if (i % 16) == 0:
346- result += "%06X: " % i
347- byte = struct.unpack("B", buffer[i])[0]
348- result += "%02X " % byte
349- if (i & 15) == 7:
350- result += " "
351- if ((i + 1) % 16) == 0 or (i + 1) == size:
352- j = i
353- while ((j + 1) % 16) != 0:
354- result += " "
355- if (j & 7) == 7:
356- result += " "
357- j += 1
358- result += " "
359- for j in range(i - (i % 16), i + 1):
360- byte = struct.unpack("B", buffer[j])[0]
361- k = '.'
362- if IsPrintable(byte):
363- k = chr(byte)
364- result += k
365- if (i + 1) != size:
366- result += "\n"
367- return result
368-
369-def ThrottleLog(counter):
370- # Log everything up to 10, every 10 up to 100, then every 100.
371- return (counter < 10) or ((counter < 100) and ((counter % 10) == 0)) or ((counter % 100) == 0)
372-
373-def Logger():
374- class T(object):
375- def __init__(self):
376- self.File = None
377-
378- self = T()
379-
380- def LogToFile(message):
381- FilePath = ["/var/log/waagent.log", "waagent.log"][IsWindows()]
382- if not os.path.isfile(FilePath) and self.File != None:
383- self.File.close()
384- self.File = None
385- if self.File == None:
386- self.File = open(FilePath, "a")
387- self.File.write(message + "\n")
388- self.File.flush()
389-
390- def Log(message):
391- LogWithPrefix("", message)
392-
393- def LogWithPrefix(prefix, message):
394- t = time.localtime()
395- t = "%04u/%02u/%02u %02u:%02u:%02u " % (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
396- t += prefix
397- for line in message.split('\n'):
398- line = t + line
399- print(line)
400- LogToFile(line)
401-
402- return Log, LogWithPrefix
403-
404-Log, LogWithPrefix = Logger()
405-
406-def NoLog(message):
407- pass
408-
409-def LogIfVerbose(message):
410- if Verbose == True:
411- Log(message)
412-
413-def LogWithPrefixIfVerbose(prefix, message):
414- if Verbose == True:
415- LogWithPrefix(prefix, message)
416-
417-def Warn(message):
418- LogWithPrefix("WARNING:", message)
419-
420-def Error(message):
421- LogWithPrefix("ERROR:", message)
422-
423-def ErrorWithPrefix(prefix, message):
424- LogWithPrefix("ERROR:" + prefix, message)
425-
426-def Linux_ioctl_GetIpv4Address(ifname):
427- import fcntl
428- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
429- return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
430-
431-def Linux_ioctl_GetInterfaceMac(ifname):
432- import fcntl
433- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
434- info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15]))
435- return ''.join(['%02X' % Ord(char) for char in info[18:24]])
436-
437-def GetIpv4Address():
438- if IsLinux():
439- for ifname in PossibleEthernetInterfaces:
440- try:
441- return Linux_ioctl_GetIpv4Address(ifname)
442- except IOError, e:
443- pass
444- else:
445- try:
446- return socket.gethostbyname(socket.gethostname())
447- except Exception, e:
448- ErrorWithPrefix("GetIpv4Address:", str(e))
449- ErrorWithPrefix("GetIpv4Address:", traceback.format_exc())
450-
451-def HexStringToByteArray(a):
452- b = ""
453- for c in range(0, len(a) / 2):
454- b += struct.pack("B", int(a[c * 2:c * 2 + 2], 16))
455- return b
456-
457-def GetMacAddress():
458- if IsWindows():
459- # Windows: Physical Address. . . . . . . . . : 00-15-17-79-00-7F\n
460- a = "ipconfig /all | findstr /c:\"Physical Address\" | findstr /v \"00-00-00-00-00-00-00\""
461- a = os.popen(a).read()
462- a = re.sub("\s+$", "", a)
463- a = re.sub(".+ ", "", a)
464- a = re.sub(":", "", a)
465- a = re.sub("-", "", a)
466- else:
467- for ifname in PossibleEthernetInterfaces:
468- try:
469- a = Linux_ioctl_GetInterfaceMac(ifname)
470- break
471- except IOError, e:
472- pass
473- return HexStringToByteArray(a)
474-
475-def DeviceForIdePort(n):
476- if n > 3:
477- return None
478- g0 = "00000000"
479- if n > 1:
480- g0 = "00000001"
481- n = n - 2
482- device = None
483- path="/sys/bus/vmbus/devices/"
484- for vmbus in os.listdir(path):
485- guid=GetFileContents(path + vmbus + "/device_id").lstrip('{').split('-')
486- if guid[0] == g0 and guid[1] == "000" + str(n):
487- for root, dirs, files in os.walk(path + vmbus):
488- if root.endswith("/block"):
489- device = dirs[0]
490- break
491- break
492- return device
493-
494-class Util(object):
495- def _HttpGet(self, url, headers):
496- LogIfVerbose("HttpGet(" + url + ")")
497- maxRetry = 2
498- if url.startswith("http://"):
499- url = url[7:]
500- url = url[url.index("/"):]
501- for retry in range(0, maxRetry + 1):
502- strRetry = str(retry)
503- log = [NoLog, Log][retry > 0]
504- log("retry HttpGet(" + url + "),retry=" + strRetry)
505- response = None
506- strStatus = "None"
507- try:
508- httpConnection = httplib.HTTPConnection(self.Endpoint)
509- if headers == None:
510- request = httpConnection.request("GET", url)
511- else:
512- request = httpConnection.request("GET", url, None, headers)
513- response = httpConnection.getresponse()
514- strStatus = str(response.status)
515- except:
516- pass
517- log("response HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
518- if response == None or response.status != httplib.OK:
519- Error("HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
520- if retry == maxRetry:
521- Log("return HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
522- return None
523- else:
524- Log("sleep 10 seconds HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
525- time.sleep(10)
526- else:
527- log("return HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
528- return response.read()
529-
530- def HttpGetWithoutHeaders(self, url):
531- return self._HttpGet(url, None)
532-
533- def HttpGetWithHeaders(self, url):
534- return self._HttpGet(url, {"x-ms-agent-name": GuestAgentName, "x-ms-version": ProtocolVersion})
535-
536- def HttpSecureGetWithHeaders(self, url, transportCert):
537- return self._HttpGet(url, {"x-ms-agent-name": GuestAgentName,
538- "x-ms-version": ProtocolVersion,
539- "x-ms-cipher-name": "DES_EDE3_CBC",
540- "x-ms-guest-agent-public-x509-cert": transportCert})
541-
542- def HttpPost(self, url, data):
543- LogIfVerbose("HttpPost(" + url + ")")
544- maxRetry = 2
545- for retry in range(0, maxRetry + 1):
546- strRetry = str(retry)
547- log = [NoLog, Log][retry > 0]
548- log("retry HttpPost(" + url + "),retry=" + strRetry)
549- response = None
550- strStatus = "None"
551- try:
552- httpConnection = httplib.HTTPConnection(self.Endpoint)
553- request = httpConnection.request("POST", url, data, {"x-ms-agent-name": GuestAgentName,
554- "Content-Type": "text/xml; charset=utf-8",
555- "x-ms-version": ProtocolVersion})
556- response = httpConnection.getresponse()
557- strStatus = str(response.status)
558- except:
559- pass
560- log("response HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
561- if response == None or (response.status != httplib.OK and response.status != httplib.ACCEPTED):
562- Error("HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
563- if retry == maxRetry:
564- Log("return HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
565- return None
566- else:
567- Log("sleep 10 seconds HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
568- time.sleep(10)
569- else:
570- log("return HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
571- return response
572-
573-def LoadBalancerProbeServer(port):
574-
575- class T(object):
576- def __init__(self, port):
577- enabled = Config.get("LBProbeResponder")
578- if enabled != None and enabled.lower().startswith("n"):
579- return
580- self.ProbeCounter = 0
581- self.server = SocketServer.TCPServer((GetIpv4Address(), port), TCPHandler)
582- self.server_thread = threading.Thread(target = self.server.serve_forever)
583- self.server_thread.setDaemon(True)
584- self.server_thread.start()
585-
586- def shutdown(self):
587- global EnableLoadBalancerProbes
588- if not EnableLoadBalancerProbes:
589- return
590- self.server.shutdown()
591-
592- class TCPHandler(SocketServer.BaseRequestHandler):
593- def GetHttpDateTimeNow(self):
594- # Date: Fri, 25 Mar 2011 04:53:10 GMT
595- return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
596-
597- def handle(self):
598- context.ProbeCounter = (context.ProbeCounter + 1) % 1000000
599- log = [NoLog, LogIfVerbose][ThrottleLog(context.ProbeCounter)]
600- strCounter = str(context.ProbeCounter)
601- if context.ProbeCounter == 1:
602- Log("Receiving LB probes.")
603- log("Received LB probe # " + strCounter)
604- self.request.recv(1024)
605- self.request.send("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/html\r\nDate: " + self.GetHttpDateTimeNow() + "\r\n\r\nOK")
606-
607- context = T(port)
608- return context
609-
610-class ConfigurationProvider(object):
611- def __init__(self):
612- self.values = dict()
613- if os.path.isfile("/etc/waagent.conf") == False:
614- raise Exception("Missing configuration in /etc/waagent.conf")
615- try:
616- for line in GetFileContents("/etc/waagent.conf").split('\n'):
617- if not line.startswith("#") and "=" in line:
618- parts = line.split()[0].split('=')
619- value = parts[1].strip("\" ")
620- if value != "None":
621- self.values[parts[0]] = value
622- else:
623- self.values[parts[0]] = None
624- except:
625- Error("Unable to parse /etc/waagent.conf")
626- raise
627- return
628-
629- def get(self, key):
630- return self.values.get(key)
631-
632-class EnvMonitor(object):
633- def __init__(self):
634- self.shutdown = False
635- self.HostName = socket.gethostname()
636- self.server_thread = threading.Thread(target = self.monitor)
637- self.server_thread.setDaemon(True)
638- self.server_thread.start()
639- self.published = False
640-
641- def monitor(self):
642- publish = Config.get("Provisioning.MonitorHostName")
643- dhcpcmd = "pidof dhclient"
644- if IsSuse():
645- dhcpcmd = "pidof dhcpcd"
646- if IsDebian():
647- dhcpcmd = "pidof dhclient3"
648- dhcppid = os.popen(dhcpcmd).read()
649- while not self.shutdown:
650- for a in RulesFiles:
651- if os.path.isfile(a):
652- if os.path.isfile(GetLastPathElement(a)):
653- os.remove(GetLastPathElement(a))
654- shutil.move(a, ".")
655- Log("EnvMonitor: Moved " + a + " -> " + LibDir)
656- if publish != None and publish.lower().startswith("y"):
657- try:
658- if socket.gethostname() != self.HostName:
659- Log("EnvMonitor: Detected host name change: " + self.HostName + " -> " + socket.gethostname())
660- self.HostName = socket.gethostname()
661- WaAgent.UpdateAndPublishHostName(self.HostName)
662- dhcppid = os.popen(dhcpcmd).read()
663- self.published = True
664- except:
665- pass
666- else:
667- self.published = True
668- pid = ""
669- if not os.path.isdir("/proc/" + dhcppid.strip()):
670- pid = os.popen(dhcpcmd).read()
671- if pid != "" and pid != dhcppid:
672- Log("EnvMonitor: Detected dhcp client restart. Restoring routing table.")
673- WaAgent.RestoreRoutes()
674- dhcppid = pid
675- time.sleep(5)
676-
677- def SetHostName(self, name):
678- if socket.gethostname() == name:
679- self.published = True
680- else:
681- Run("hostname " + name)
682-
683- def IsNamePublished(self):
684- return self.published
685-
686- def shutdown(self):
687- self.shutdown = True
688- self.server_thread.join()
689-
690-class Certificates(object):
691-#
692-# <CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd">
693-# <Version>2010-12-15</Version>
694-# <Incarnation>2</Incarnation>
695-# <Format>Pkcs7BlobWithPfxContents</Format>
696-# <Data>MIILTAY...
697-# </Data>
698-# </CertificateFile>
699-#
700- def __init__(self):
701- self.reinitialize()
702-
703- def reinitialize(self):
704- self.Incarnation = None
705- self.Role = None
706-
707- def Parse(self, xmlText):
708- self.reinitialize()
709- SetFileContents("Certificates.xml", xmlText)
710- dom = xml.dom.minidom.parseString(xmlText)
711- for a in [ "CertificateFile", "Version", "Incarnation",
712- "Format", "Data", ]:
713- if not dom.getElementsByTagName(a):
714- Error("Certificates.Parse: Missing " + a)
715- return None
716- node = dom.childNodes[0]
717- if node.localName != "CertificateFile":
718- Error("Certificates.Parse: root not CertificateFile")
719- return None
720- SetFileContents("Certificates.p7m",
721- "MIME-Version: 1.0\n"
722- + "Content-Disposition: attachment; filename=\"Certificates.p7m\"\n"
723- + "Content-Type: application/x-pkcs7-mime; name=\"Certificates.p7m\"\n"
724- + "Content-Transfer-Encoding: base64\n\n"
725- + GetNodeTextData(dom.getElementsByTagName("Data")[0]))
726- if Run(Openssl + " cms -decrypt -in Certificates.p7m -inkey TransportPrivate.pem -recip TransportCert.pem | " + Openssl + " pkcs12 -nodes -password pass: -out Certificates.pem"):
727- Error("Certificates.Parse: Failed to extract certificates from CMS message.")
728- return self
729- # There may be multiple certificates in this package. Split them.
730- file = open("Certificates.pem")
731- pindex = 1
732- cindex = 1
733- output = open("temp.pem", "w")
734- for line in file.readlines():
735- output.write(line)
736- if line.startswith("-----END PRIVATE KEY-----") or line.startswith("-----END CERTIFICATE-----"):
737- output.close()
738- if line.startswith("-----END PRIVATE KEY-----"):
739- os.rename("temp.pem", str(pindex) + ".prv")
740- pindex += 1
741- else:
742- os.rename("temp.pem", str(cindex) + ".crt")
743- cindex += 1
744- output = open("temp.pem", "w")
745- output.close()
746- os.remove("temp.pem")
747- keys = dict()
748- index = 1
749- filename = str(index) + ".crt"
750- while os.path.isfile(filename):
751- thumbprint = os.popen(Openssl + " x509 -in " + filename + " -fingerprint -noout").read().rstrip().split('=')[1].replace(':', '').upper()
752- pubkey=os.popen(Openssl + " x509 -in " + filename + " -pubkey -noout").read()
753- keys[pubkey] = thumbprint
754- os.rename(filename, thumbprint + ".crt")
755- os.chmod(thumbprint + ".crt", 0600)
756- if IsRedHat():
757- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + thumbprint + ".crt")
758- index += 1
759- filename = str(index) + ".crt"
760- index = 1
761- filename = str(index) + ".prv"
762- while os.path.isfile(filename):
763- pubkey = os.popen(Openssl + " rsa -in " + filename + " -pubout").read()
764- os.rename(filename, keys[pubkey] + ".prv")
765- os.chmod(keys[pubkey] + ".prv", 0600)
766- if IsRedHat():
767- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + keys[pubkey] + ".prv")
768- index += 1
769- filename = str(index) + ".prv"
770- return self
771-
772-class SharedConfig(object):
773-#
774-# <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
775-# <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2">
776-# <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" />
777-# <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" />
778-# </Deployment>
779-# <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" />
780-# <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" settleTimeSeconds="10" />
781-# <LoadBalancerSettings timeoutSeconds="0" waitLoadBalancerProbeCount="8">
782-# <Probes>
783-# <Probe name="MachineRole" />
784-# <Probe name="55B17C5E41A1E1E8FA991CF80FAC8E55" />
785-# <Probe name="3EA4DBC19418F0A766A4C19D431FA45F" />
786-# </Probes>
787-# </LoadBalancerSettings>
788-# <OutputEndpoints>
789-# <Endpoint name="MachineRole:Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" type="SFS">
790-# <Target instance="MachineRole_IN_0" endpoint="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" />
791-# </Endpoint>
792-# </OutputEndpoints>
793-# <Instances>
794-# <Instance id="MachineRole_IN_0" address="10.115.153.75">
795-# <FaultDomains randomId="0" updateId="0" updateCount="0" />
796-# <InputEndpoints>
797-# <Endpoint name="a" address="10.115.153.75:80" protocol="http" isPublic="true" loadBalancedPublicAddress="70.37.106.197:80" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
798-# <LocalPorts>
799-# <LocalPortRange from="80" to="80" />
800-# </LocalPorts>
801-# </Endpoint>
802-# <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" address="10.115.153.75:3389" protocol="tcp" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
803-# <LocalPorts>
804-# <LocalPortRange from="3389" to="3389" />
805-# </LocalPorts>
806-# </Endpoint>
807-# <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" address="10.115.153.75:20000" protocol="tcp" isPublic="true" loadBalancedPublicAddress="70.37.106.197:3389" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
808-# <LocalPorts>
809-# <LocalPortRange from="20000" to="20000" />
810-# </LocalPorts>
811-# </Endpoint>
812-# </InputEndpoints>
813-# </Instance>
814-# </Instances>
815-# </SharedConfig>
816-#
817- def __init__(self):
818- self.reinitialize()
819-
820- def reinitialize(self):
821- self.Deployment = None
822- self.Incarnation = None
823- self.Role = None
824- self.LoadBalancerSettings = None
825- self.OutputEndpoints = None
826- self.Instances = None
827-
828- def Parse(self, xmlText):
829- self.reinitialize()
830- SetFileContents("SharedConfig.xml", xmlText)
831- dom = xml.dom.minidom.parseString(xmlText)
832- for a in [ "SharedConfig", "Deployment", "Service",
833- "ServiceInstance", "Incarnation", "Role", ]:
834- if not dom.getElementsByTagName(a):
835- Error("SharedConfig.Parse: Missing " + a)
836- return None
837- node = dom.childNodes[0]
838- if node.localName != "SharedConfig":
839- Error("SharedConfig.Parse: root not SharedConfig")
840- return None
841- program = Config.get("Role.TopologyConsumer")
842- if program != None:
843- os.spawnl(os.P_NOWAIT, program, program, LibDir + "/SharedConfig.xml")
844- return self
845-
846-class HostingEnvironmentConfig(object):
847-#
848-# <HostingEnvironmentConfig version="1.0.0.0" goalStateIncarnation="1">
849-# <StoredCertificates>
850-# <StoredCertificate name="Stored0Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" certificateId="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" storeName="My" configurationLevel="System" />
851-# </StoredCertificates>
852-# <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2">
853-# <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" />
854-# <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" />
855-# </Deployment>
856-# <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" />
857-# <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" hostingEnvironmentVersion="1" software="" softwareType="ApplicationPackage" entryPoint="" parameters="" settleTimeSeconds="10" />
858-# <HostingEnvironmentSettings name="full" Runtime="rd_fabric_stable.110217-1402.RuntimePackage_1.0.0.8.zip">
859-# <CAS mode="full" />
860-# <PrivilegeLevel mode="max" />
861-# <AdditionalProperties><CgiHandlers></CgiHandlers></AdditionalProperties>
862-# </HostingEnvironmentSettings>
863-# <ApplicationSettings>
864-# <Setting name="__ModelData" value="&lt;m role=&quot;MachineRole&quot; xmlns=&quot;urn:azure:m:v1&quot;>&lt;r name=&quot;MachineRole&quot;>&lt;e name=&quot;a&quot; />&lt;e name=&quot;b&quot; />&lt;e name=&quot;Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp&quot; />&lt;e name=&quot;Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput&quot; />&lt;/r>&lt;/m>" />
865-# <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=http;AccountName=osimages;AccountKey=DNZQ..." />
866-# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="MIIBnQYJKoZIhvcN..." />
867-# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2022-07-23T23:59:59.0000000-07:00" />
868-# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="test" />
869-# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="true" />
870-# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" />
871-# <Setting name="Certificate|Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" value="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" />
872-# </ApplicationSettings>
873-# <ResourceReferences>
874-# <Resource name="DiagnosticStore" type="directory" request="Microsoft.Cis.Fabric.Controller.Descriptions.ServiceDescription.Data.Policy" sticky="true" size="1" path="db00a7755a5e4e8a8fe4b19bc3b330c3.MachineRole.DiagnosticStore\" disableQuota="false" />
875-# </ResourceReferences>
876-# </HostingEnvironmentConfig>
877-#
878- def __init__(self):
879- self.reinitialize()
880-
881- def reinitialize(self):
882- self.StoredCertificates = None
883- self.Deployment = None
884- self.Incarnation = None
885- self.Role = None
886- self.HostingEnvironmentSettings = None
887- self.ApplicationSettings = None
888- self.Certificates = None
889- self.ResourceReferences = None
890-
891- def Parse(self, xmlText):
892- self.reinitialize()
893- SetFileContents("HostingEnvironmentConfig.xml", xmlText)
894- dom = xml.dom.minidom.parseString(xmlText)
895- for a in [ "HostingEnvironmentConfig", "Deployment", "Service",
896- "ServiceInstance", "Incarnation", "Role", ]:
897- if not dom.getElementsByTagName(a):
898- Error("HostingEnvironmentConfig.Parse: Missing " + a)
899- return None
900- node = dom.childNodes[0]
901- if node.localName != "HostingEnvironmentConfig":
902- Error("HostingEnvironmentConfig.Parse: root not HostingEnvironmentConfig")
903- return None
904- self.ApplicationSettings = dom.getElementsByTagName("Setting")
905- self.Certificates = dom.getElementsByTagName("StoredCertificate")
906- return self
907-
908- def DecryptPassword(self, e):
909- SetFileContents("password.p7m",
910- "MIME-Version: 1.0\n"
911- + "Content-Disposition: attachment; filename=\"password.p7m\"\n"
912- + "Content-Type: application/x-pkcs7-mime; name=\"password.p7m\"\n"
913- + "Content-Transfer-Encoding: base64\n\n"
914- + textwrap.fill(e, 64))
915- return os.popen(Openssl + " cms -decrypt -in password.p7m -inkey Certificates.pem -recip Certificates.pem").read()
916-
917- def ActivateResourceDisk(self):
918- global DiskActivated
919- if IsWindows():
920- DiskActivated = True
921- Log("Skipping ActivateResourceDisk on Windows")
922- return
923- format = Config.get("ResourceDisk.Format")
924- if format == None or format.lower().startswith("n"):
925- DiskActivated = True
926- return
927- device = DeviceForIdePort(1)
928- if device == None:
929- Error("ActivateResourceDisk: Unable to detect disk topology.")
930- return
931- device = "/dev/" + device
932- for entry in os.popen("mount").read().split():
933- if entry.startswith(device + "1"):
934- Log("ActivateResourceDisk: " + device + "1 is already mounted.")
935- DiskActivated = True
936- return
937- mountpoint = Config.get("ResourceDisk.MountPoint")
938- if mountpoint == None:
939- mountpoint = "/mnt/resource"
940- CreateDir(mountpoint, "root", 0755)
941- fs = Config.get("ResourceDisk.Filesystem")
942- if fs == None:
943- fs = "ext3"
944- if os.popen("sfdisk -q -c " + device + " 1").read().rstrip() == "7" and fs != "ntfs":
945- Run("sfdisk -c " + device + " 1 83")
946- Run("mkfs." + fs + " " + device + "1")
947- if Run("mount " + device + "1 " + mountpoint):
948- Error("ActivateResourceDisk: Failed to mount resource disk (" + device + "1).")
949- return
950- Log("Resource disk (" + device + "1) is mounted at " + mountpoint + " with fstype " + fs)
951- DiskActivated = True
952- swap = Config.get("ResourceDisk.EnableSwap")
953- if swap == None or swap.lower().startswith("n"):
954- return
955- sizeKB = int(Config.get("ResourceDisk.SwapSizeMB")) * 1024
956- if os.path.isfile(mountpoint + "/swapfile") and os.path.getsize(mountpoint + "/swapfile") != (sizeKB * 1024):
957- os.remove(mountpoint + "/swapfile")
958- if not os.path.isfile(mountpoint + "/swapfile"):
959- Run("dd if=/dev/zero of=" + mountpoint + "/swapfile bs=1024 count=" + str(sizeKB))
960- Run("mkswap " + mountpoint + "/swapfile")
961- if not Run("swapon " + mountpoint + "/swapfile"):
962- Log("Enabled " + str(sizeKB) + " KB of swap at " + mountpoint + "/swapfile")
963- else:
964- Error("ActivateResourceDisk: Failed to activate swap at " + mountpoint + "/swapfile")
965-
966- def Process(self):
967- if DiskActivated == False:
968- diskThread = threading.Thread(target = self.ActivateResourceDisk)
969- diskThread.start()
970- User = None
971- Pass = None
972- Expiration = None
973- Thumbprint = None
974- for b in self.ApplicationSettings:
975- sname = b.getAttribute("name")
976- svalue = b.getAttribute("value")
977- if sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword":
978- Pass = self.DecryptPassword(svalue)
979- elif sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername":
980- User = svalue
981- elif sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration":
982- Expiration = svalue
983- elif sname == "Certificate|Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption":
984- Thumbprint = svalue.split(':')[1].upper()
985- if User != None and Pass != None:
986- if User != "root" and User != "" and Pass != "":
987- CreateAccount(User, Pass, Expiration, Thumbprint)
988- else:
989- Error("Not creating user account: user=" + User + " pass=" + Pass)
990- for c in self.Certificates:
991- cname = c.getAttribute("name")
992- csha1 = c.getAttribute("certificateId").split(':')[1].upper()
993- cpath = c.getAttribute("storeName")
994- clevel = c.getAttribute("configurationLevel")
995- if os.path.isfile(csha1 + ".prv"):
996- Log("Private key with thumbprint: " + csha1 + " was retrieved.")
997- if os.path.isfile(csha1 + ".crt"):
998- Log("Public cert with thumbprint: " + csha1 + " was retrieved.")
999- program = Config.get("Role.ConfigurationConsumer")
1000- if program != None:
1001- os.spawnl(os.P_NOWAIT, program, program, LibDir + "/HostingEnvironmentConfig.xml")
1002-
1003-class GoalState(Util):
1004-#
1005-# <GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
1006-# <Version>2010-12-15</Version>
1007-# <Incarnation>1</Incarnation>
1008-# <Machine>
1009-# <ExpectedState>Started</ExpectedState>
1010-# <LBProbePorts>
1011-# <Port>16001</Port>
1012-# </LBProbePorts>
1013-# </Machine>
1014-# <Container>
1015-# <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId>
1016-# <RoleInstanceList>
1017-# <RoleInstance>
1018-# <InstanceId>MachineRole_IN_0</InstanceId>
1019-# <State>Started</State>
1020-# <Configuration>
1021-# <HostingEnvironmentConfig>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=config&amp;type=hostingEnvironmentConfig&amp;incarnation=1</HostingEnvironmentConfig>
1022-# <SharedConfig>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=config&amp;type=sharedConfig&amp;incarnation=1</SharedConfig>
1023-# <Certificates>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=certificates&amp;incarnation=1</Certificates>
1024-# </Configuration>
1025-# </RoleInstance>
1026-# </RoleInstanceList>
1027-# </Container>
1028-# </GoalState>
1029-#
1030-# There is only one Role for VM images.
1031-#
1032-# Of primary interest is:
1033-# Machine/ExpectedState -- this is how shutdown is requested
1034-# LBProbePorts -- an http server needs to run here
1035-# We also note Container/ContainerID and RoleInstance/InstanceId to form the health report.
1036-# And of course, Incarnation
1037-#
1038- def __init__(self, Agent):
1039- self.Agent = Agent
1040- self.Endpoint = Agent.Endpoint
1041- self.TransportCert = Agent.TransportCert
1042- self.reinitialize()
1043-
1044- def reinitialize(self):
1045- self.Incarnation = None # integer
1046- self.ExpectedState = None # "Started" or "Stopped"
1047- self.HostingEnvironmentConfigUrl = None
1048- self.HostingEnvironmentConfigXml = None
1049- self.HostingEnvironmentConfig = None
1050- self.SharedConfigUrl = None
1051- self.SharedConfigXml = None
1052- self.SharedConfig = None
1053- self.CertificatesUrl = None
1054- self.CertificatesXml = None
1055- self.Certificates = None
1056- self.RoleInstanceId = None
1057- self.ContainerId = None
1058- self.LoadBalancerProbePort = None # integer, ?list of integers
1059-
1060- def Parse(self, xmlText):
1061- self.reinitialize()
1062- node = xml.dom.minidom.parseString(xmlText).childNodes[0]
1063- if node.localName != "GoalState":
1064- Error("GoalState.Parse: root not GoalState")
1065- return None
1066- for a in node.childNodes:
1067- if a.nodeType == node.ELEMENT_NODE:
1068- if a.localName == "Incarnation":
1069- self.Incarnation = GetNodeTextData(a)
1070- elif a.localName == "Machine":
1071- for b in a.childNodes:
1072- if b.nodeType == node.ELEMENT_NODE:
1073- if b.localName == "ExpectedState":
1074- self.ExpectedState = GetNodeTextData(b)
1075- Log("ExpectedState: " + self.ExpectedState)
1076- elif b.localName == "LBProbePorts":
1077- for c in b.childNodes:
1078- if c.nodeType == node.ELEMENT_NODE and c.localName == "Port":
1079- self.LoadBalancerProbePort = int(GetNodeTextData(c))
1080- elif a.localName == "Container":
1081- for b in a.childNodes:
1082- if b.nodeType == node.ELEMENT_NODE:
1083- if b.localName == "ContainerId":
1084- self.ContainerId = GetNodeTextData(b)
1085- Log("ContainerId: " + self.ContainerId)
1086- elif b.localName == "RoleInstanceList":
1087- for c in b.childNodes:
1088- if c.localName == "RoleInstance":
1089- for d in c.childNodes:
1090- if d.nodeType == node.ELEMENT_NODE:
1091- if d.localName == "InstanceId":
1092- self.RoleInstanceId = GetNodeTextData(d)
1093- Log("RoleInstanceId: " + self.RoleInstanceId)
1094- elif d.localName == "State":
1095- pass
1096- elif d.localName == "Configuration":
1097- for e in d.childNodes:
1098- if e.nodeType == node.ELEMENT_NODE:
1099- if e.localName == "HostingEnvironmentConfig":
1100- self.HostingEnvironmentConfigUrl = GetNodeTextData(e)
1101- LogIfVerbose("HostingEnvironmentConfigUrl:" + self.HostingEnvironmentConfigUrl)
1102- self.HostingEnvironmentConfigXml = self.HttpGetWithHeaders(self.HostingEnvironmentConfigUrl)
1103- self.HostingEnvironmentConfig = HostingEnvironmentConfig().Parse(self.HostingEnvironmentConfigXml)
1104- elif e.localName == "SharedConfig":
1105- self.SharedConfigUrl = GetNodeTextData(e)
1106- LogIfVerbose("SharedConfigUrl:" + self.SharedConfigUrl)
1107- self.SharedConfigXml = self.HttpGetWithHeaders(self.SharedConfigUrl)
1108- self.SharedConfig = SharedConfig().Parse(self.SharedConfigXml)
1109- elif e.localName == "Certificates":
1110- self.CertificatesUrl = GetNodeTextData(e)
1111- LogIfVerbose("CertificatesUrl:" + self.CertificatesUrl)
1112- self.CertificatesXml = self.HttpSecureGetWithHeaders(self.CertificatesUrl, self.TransportCert)
1113- self.Certificates = Certificates().Parse(self.CertificatesXml)
1114- if self.Incarnation == None:
1115- Error("GoalState.Parse: Incarnation missing")
1116- return None
1117- if self.ExpectedState == None:
1118- Error("GoalState.Parse: ExpectedState missing")
1119- return None
1120- if self.RoleInstanceId == None:
1121- Error("GoalState.Parse: RoleInstanceId missing")
1122- return None
1123- if self.ContainerId == None:
1124- Error("GoalState.Parse: ContainerId missing")
1125- return None
1126- SetFileContents("GoalState." + self.Incarnation + ".xml", xmlText)
1127- return self
1128-
1129- def Process(self):
1130- self.HostingEnvironmentConfig.Process()
1131-
1132-class OvfEnv(object):
1133-#
1134-# <?xml version="1.0" encoding="utf-8"?>
1135-# <Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1136-# <wa:ProvisioningSection>
1137-# <wa:Version>1.0</wa:Version>
1138-# <LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
1139-# <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
1140-# <HostName>HostName</HostName>
1141-# <UserName>UserName</UserName>
1142-# <UserPassword>UserPassword</UserPassword>
1143-# <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication>
1144-# <SSH>
1145-# <PublicKeys>
1146-# <PublicKey>
1147-# <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
1148-# <Path>$HOME/UserName/.ssh/authorized_keys</Path>
1149-# </PublicKey>
1150-# </PublicKeys>
1151-# <KeyPairs>
1152-# <KeyPair>
1153-# <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
1154-# <Path>$HOME/UserName/.ssh/id_rsa</Path>
1155-# </KeyPair>
1156-# </KeyPairs>
1157-# </SSH>
1158-# </LinuxProvisioningConfigurationSet>
1159-# </wa:ProvisioningSection>
1160-# </Environment>
1161-#
1162- def __init__(self):
1163- self.reinitialize()
1164-
1165- def reinitialize(self):
1166- self.WaNs = "http://schemas.microsoft.com/windowsazure"
1167- self.OvfNs = "http://schemas.dmtf.org/ovf/environment/1"
1168- self.MajorVersion = 1
1169- self.MinorVersion = 0
1170- self.ComputerName = None
1171- self.AdminPassword = None
1172- self.UserName = None
1173- self.UserPassword = None
1174- self.DisableSshPasswordAuthentication = True
1175- self.SshPublicKeys = []
1176- self.SshKeyPairs = []
1177-
1178- def Parse(self, xmlText):
1179- self.reinitialize()
1180- dom = xml.dom.minidom.parseString(xmlText)
1181- if len(dom.getElementsByTagNameNS(self.OvfNs, "Environment")) != 1:
1182- Error("Unable to parse OVF XML.")
1183- section = None
1184- newer = False
1185- for p in dom.getElementsByTagNameNS(self.WaNs, "ProvisioningSection"):
1186- for n in p.childNodes:
1187- if n.localName == "Version":
1188- verparts = GetNodeTextData(n).split('.')
1189- major = int(verparts[0])
1190- minor = int(verparts[1])
1191- if major > self.MajorVersion:
1192- newer = True
1193- if major != self.MajorVersion:
1194- break
1195- if minor > self.MinorVersion:
1196- newer = True
1197- section = p
1198- if newer == True:
1199- Warn("Newer provisioning configuration detected. Please consider updating waagent.")
1200- if section == None:
1201- Error("Could not find ProvisioningSection with major version=" + str(self.MajorVersion))
1202- return None
1203- self.ComputerName = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "HostName")[0])
1204- self.UserName = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "UserName")[0])
1205- try:
1206- self.UserPassword = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "UserPassword")[0])
1207- except:
1208- pass
1209- disableSshPass = section.getElementsByTagNameNS(self.WaNs, "DisableSshPasswordAuthentication")
1210- if len(disableSshPass) != 0:
1211- self.DisableSshPasswordAuthentication = (GetNodeTextData(disableSshPass[0]).lower() == "true")
1212- for pkey in section.getElementsByTagNameNS(self.WaNs, "PublicKey"):
1213- fp = None
1214- path = None
1215- for c in pkey.childNodes:
1216- if c.localName == "Fingerprint":
1217- fp = GetNodeTextData(c).upper()
1218- if c.localName == "Path":
1219- path = GetNodeTextData(c)
1220- self.SshPublicKeys += [[fp, path]]
1221- for keyp in section.getElementsByTagNameNS(self.WaNs, "KeyPair"):
1222- fp = None
1223- path = None
1224- for c in keyp.childNodes:
1225- if c.localName == "Fingerprint":
1226- fp = GetNodeTextData(c).upper()
1227- if c.localName == "Path":
1228- path = GetNodeTextData(c)
1229- self.SshKeyPairs += [[fp, path]]
1230- return self
1231-
1232- def PrepareDir(self, filepath):
1233- home = GetHome()
1234- # Expand HOME variable if present in path
1235- path = os.path.normpath(filepath.replace("$HOME", home))
1236- if (path.startswith("/") == False) or (path.endswith("/") == True):
1237- return None
1238- dir = path.rsplit('/', 1)[0]
1239- if dir != "":
1240- CreateDir(dir, "root", 0700)
1241- if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
1242- ChangeOwner(dir, self.UserName)
1243- return path
1244-
1245- def NumberToBytes(self, i):
1246- result = []
1247- while i:
1248- result.append(chr(i&0xFF))
1249- i >>= 8
1250- result.reverse()
1251- return ''.join(result)
1252-
1253- def BitsToString(self, a):
1254- index=7
1255- s = ""
1256- c = 0
1257- for bit in a:
1258- c = c | (bit << index)
1259- index = index - 1
1260- if index == -1:
1261- s = s + struct.pack('>B', c)
1262- c = 0
1263- index = 7
1264- return s
1265-
1266- def OpensslToSsh(self, file):
1267- from pyasn1.codec.der import decoder as der_decoder
1268- try:
1269- f = open(file).read().replace('\n','').split("KEY-----")[1].split('-')[0]
1270- k=der_decoder.decode(self.BitsToString(der_decoder.decode(base64.b64decode(f))[0][1]))[0]
1271- n=k[0]
1272- e=k[1]
1273- keydata=""
1274- keydata += struct.pack('>I',len("ssh-rsa"))
1275- keydata += "ssh-rsa"
1276- keydata += struct.pack('>I',len(self.NumberToBytes(e)))
1277- keydata += self.NumberToBytes(e)
1278- keydata += struct.pack('>I',len(self.NumberToBytes(n)) + 1)
1279- keydata += "\0"
1280- keydata += self.NumberToBytes(n)
1281- except Exception, e:
1282- print("OpensslToSsh: Exception " + str(e))
1283- return None
1284- return "ssh-rsa " + base64.b64encode(keydata) + "\n"
1285-
1286- def Process(self):
1287- error = None
1288- WaAgent.EnvMonitor.SetHostName(self.ComputerName)
1289- if self.DisableSshPasswordAuthentication:
1290- filepath = "/etc/ssh/sshd_config"
1291- # Disable RFC 4252 and RFC 4256 authentication schemes.
1292- ReplaceFileContentsAtomic(filepath, "\n".join(filter(lambda a: not
1293- (a.startswith("PasswordAuthentication") or a.startswith("ChallengeResponseAuthentication")),
1294- GetFileContents(filepath).split('\n'))) + "PasswordAuthentication no\nChallengeResponseAuthentication no\n")
1295- Log("Disabled SSH password-based authentication methods.")
1296- if self.AdminPassword != None:
1297- os.popen("chpasswd", "w").write("root:" + self.AdminPassword + "\n")
1298- if self.UserName != None:
1299- error = CreateAccount(self.UserName, self.UserPassword, None, None)
1300- sel = os.popen("getenforce").read().startswith("Enforcing")
1301- if sel == True and IsRedHat():
1302- Run("setenforce 0")
1303- home = GetHome()
1304- for pkey in self.SshPublicKeys:
1305- if not os.path.isfile(pkey[0] + ".crt"):
1306- Error("PublicKey not found: " + pkey[0])
1307- error = "Failed to deploy public key (0x09)."
1308- continue
1309- path = self.PrepareDir(pkey[1])
1310- if path == None:
1311- Error("Invalid path: " + pkey[1] + " for PublicKey: " + pkey[0])
1312- error = "Invalid path for public key (0x03)."
1313- continue
1314- Run(Openssl + " x509 -in " + pkey[0] + ".crt -noout -pubkey > " + pkey[0] + ".pub")
1315- if IsRedHat():
1316- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + pkey[0] + ".pub")
1317- if IsUbuntu():
1318- # Only supported in new SSH releases
1319- Run("ssh-keygen -i -m PKCS8 -f " + pkey[0] + ".pub >> " + path)
1320- else:
1321- SshPubKey = self.OpensslToSsh(pkey[0] + ".pub")
1322- if SshPubKey != None:
1323- AppendFileContents(path, SshPubKey)
1324- else:
1325- Error("Failed: " + pkey[0] + ".crt -> " + path)
1326- error = "Failed to deploy public key (0x04)."
1327- if IsRedHat():
1328- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path)
1329- if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
1330- ChangeOwner(path, self.UserName)
1331- for keyp in self.SshKeyPairs:
1332- if not os.path.isfile(keyp[0] + ".prv"):
1333- Error("KeyPair not found: " + keyp[0])
1334- error = "Failed to deploy key pair (0x0A)."
1335- continue
1336- path = self.PrepareDir(keyp[1])
1337- if path == None:
1338- Error("Invalid path: " + keyp[1] + " for KeyPair: " + keyp[0])
1339- error = "Invalid path for key pair (0x05)."
1340- continue
1341- SetFileContents(path, GetFileContents(keyp[0] + ".prv"))
1342- os.chmod(path, 0600)
1343- Run("ssh-keygen -y -f " + keyp[0] + ".prv > " + path + ".pub")
1344- if IsRedHat():
1345- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path)
1346- Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path + ".pub")
1347- if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
1348- ChangeOwner(path, self.UserName)
1349- ChangeOwner(path + ".pub", self.UserName)
1350- if sel == True and IsRedHat():
1351- Run("setenforce 1")
1352- while not WaAgent.EnvMonitor.IsNamePublished():
1353- time.sleep(1)
1354- ReloadSshd()
1355- return error
1356-
1357-def UpdateAndPublishHostNameCommon(name):
1358- # RedHat
1359- if IsRedHat():
1360- filepath = "/etc/sysconfig/network"
1361- if os.path.isfile(filepath):
1362- ReplaceFileContentsAtomic(filepath, "HOSTNAME=" + name + "\n"
1363- + "\n".join(filter(lambda a: not a.startswith("HOSTNAME"), GetFileContents(filepath).split('\n'))))
1364-
1365- for ethernetInterface in PossibleEthernetInterfaces:
1366- filepath = "/etc/sysconfig/network-scripts/ifcfg-" + ethernetInterface
1367- if os.path.isfile(filepath):
1368- ReplaceFileContentsAtomic(filepath, "DHCP_HOSTNAME=" + name + "\n"
1369- + "\n".join(filter(lambda a: not a.startswith("DHCP_HOSTNAME"), GetFileContents(filepath).split('\n'))))
1370-
1371- # Debian
1372- if IsDebian():
1373- SetFileContents("/etc/hostname", name)
1374-
1375- for filepath in EtcDhcpClientConfFiles:
1376- if os.path.isfile(filepath):
1377- ReplaceFileContentsAtomic(filepath, "send host-name \"" + name + "\";\n"
1378- + "\n".join(filter(lambda a: not a.startswith("send host-name"), GetFileContents(filepath).split('\n'))))
1379-
1380- # Suse
1381- if IsSuse():
1382- SetFileContents("/etc/HOSTNAME", name)
1383-
1384-class Agent(Util):
1385- def __init__(self):
1386- self.GoalState = None
1387- self.Endpoint = None
1388- self.LoadBalancerProbeServer = None
1389- self.HealthReportCounter = 0
1390- self.TransportCert = ""
1391- self.EnvMonitor = None
1392- self.SendData = None
1393- self.DhcpResponse = None
1394-
1395- def CheckVersions(self):
1396- #<?xml version="1.0" encoding="utf-8"?>
1397- #<Versions>
1398- # <Preferred>
1399- # <Version>2010-12-15</Version>
1400- # </Preferred>
1401- # <Supported>
1402- # <Version>2010-12-15</Version>
1403- # <Version>2010-28-10</Version>
1404- # </Supported>
1405- #</Versions>
1406- global ProtocolVersion
1407- protocolVersionSeen = False
1408- node = xml.dom.minidom.parseString(self.HttpGetWithoutHeaders("/?comp=versions")).childNodes[0]
1409- if node.localName != "Versions":
1410- Error("CheckVersions: root not Versions")
1411- return False
1412- for a in node.childNodes:
1413- if a.nodeType == node.ELEMENT_NODE and a.localName == "Supported":
1414- for b in a.childNodes:
1415- if b.nodeType == node.ELEMENT_NODE and b.localName == "Version":
1416- v = GetNodeTextData(b)
1417- LogIfVerbose("Fabric supported wire protocol version: " + v)
1418- if v == ProtocolVersion:
1419- protocolVersionSeen = True
1420- if a.nodeType == node.ELEMENT_NODE and a.localName == "Preferred":
1421- v = GetNodeTextData(a.getElementsByTagName("Version")[0])
1422- LogIfVerbose("Fabric preferred wire protocol version: " + v)
1423- if ProtocolVersion < v:
1424- Warn("Newer wire protocol version detected. Please consider updating waagent.")
1425- if not protocolVersionSeen:
1426- Warn("Agent supported wire protocol version: " + ProtocolVersion + " was not advertised by Fabric.")
1427- ProtocolVersion = "2011-08-31"
1428- Log("Negotiated wire protocol version: " + ProtocolVersion)
1429- return True
1430-
1431- def Unpack(self, buffer, offset, range):
1432- result = 0
1433- for i in range:
1434- result = (result << 8) | Ord(buffer[offset + i])
1435- return result
1436-
1437- def UnpackLittleEndian(self, buffer, offset, length):
1438- return self.Unpack(buffer, offset, range(length - 1, -1, -1))
1439-
1440- def UnpackBigEndian(self, buffer, offset, length):
1441- return self.Unpack(buffer, offset, range(0, length))
1442-
1443- def HexDump3(self, buffer, offset, length):
1444- return ''.join(['%02X' % Ord(char) for char in buffer[offset:offset + length]])
1445-
1446- def HexDump2(self, buffer):
1447- return self.HexDump3(buffer, 0, len(buffer))
1448-
1449- def BuildDhcpRequest(self):
1450- #
1451- # typedef struct _DHCP {
1452- # UINT8 Opcode; /* op: BOOTREQUEST or BOOTREPLY */
1453- # UINT8 HardwareAddressType; /* htype: ethernet */
1454- # UINT8 HardwareAddressLength; /* hlen: 6 (48 bit mac address) */
1455- # UINT8 Hops; /* hops: 0 */
1456- # UINT8 TransactionID[4]; /* xid: random */
1457- # UINT8 Seconds[2]; /* secs: 0 */
1458- # UINT8 Flags[2]; /* flags: 0 or 0x8000 for broadcast */
1459- # UINT8 ClientIpAddress[4]; /* ciaddr: 0 */
1460- # UINT8 YourIpAddress[4]; /* yiaddr: 0 */
1461- # UINT8 ServerIpAddress[4]; /* siaddr: 0 */
1462- # UINT8 RelayAgentIpAddress[4]; /* giaddr: 0 */
1463- # UINT8 ClientHardwareAddress[16]; /* chaddr: 6 byte ethernet MAC address */
1464- # UINT8 ServerName[64]; /* sname: 0 */
1465- # UINT8 BootFileName[128]; /* file: 0 */
1466- # UINT8 MagicCookie[4]; /* 99 130 83 99 */
1467- # /* 0x63 0x82 0x53 0x63 */
1468- # /* options -- hard code ours */
1469- #
1470- # UINT8 MessageTypeCode; /* 53 */
1471- # UINT8 MessageTypeLength; /* 1 */
1472- # UINT8 MessageType; /* 1 for DISCOVER */
1473- # UINT8 End; /* 255 */
1474- # } DHCP;
1475- #
1476-
1477- # tuple of 244 zeros
1478- # (struct.pack_into would be good here, but requires Python 2.5)
1479- sendData = [0] * 244
1480-
1481- transactionID = os.urandom(4)
1482- macAddress = GetMacAddress()
1483-
1484- # Opcode = 1
1485- # HardwareAddressType = 1 (ethernet/MAC)
1486- # HardwareAddressLength = 6 (ethernet/MAC/48 bits)
1487- for a in range(0, 3):
1488- sendData[a] = [1, 1, 6][a]
1489-
1490- # fill in transaction id (random number to ensure response matches request)
1491- for a in range(0, 4):
1492- sendData[4 + a] = Ord(transactionID[a])
1493-
1494- LogIfVerbose("BuildDhcpRequest: transactionId:%s,%04X" % (self.HexDump2(transactionID), self.UnpackBigEndian(sendData, 4, 4)))
1495-
1496- # fill in ClientHardwareAddress
1497- for a in range(0, 6):
1498- sendData[0x1C + a] = Ord(macAddress[a])
1499-
1500- # DHCP Magic Cookie: 99, 130, 83, 99
1501- # MessageTypeCode = 53 DHCP Message Type
1502- # MessageTypeLength = 1
1503- # MessageType = DHCPDISCOVER
1504- # End = 255 DHCP_END
1505- for a in range(0, 8):
1506- sendData[0xEC + a] = [99, 130, 83, 99, 53, 1, 1, 255][a]
1507- return array.array("c", map(chr, sendData))
1508-
1509- def IntegerToIpAddressV4String(self, a):
1510- return "%u.%u.%u.%u" % ((a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF)
1511-
1512- def RouteAdd(self, net, mask, gateway):
1513- if IsWindows():
1514- return
1515- net = self.IntegerToIpAddressV4String(net)
1516- mask = self.IntegerToIpAddressV4String(mask)
1517- gateway = self.IntegerToIpAddressV4String(gateway)
1518- Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway)
1519-
1520- def HandleDhcpResponse(self, sendData, receiveBuffer):
1521- LogIfVerbose("HandleDhcpResponse")
1522- bytesReceived = len(receiveBuffer)
1523- if bytesReceived < 0xF6:
1524- Error("HandleDhcpResponse: Too few bytes received " + str(bytesReceived))
1525- return None
1526-
1527- LogIfVerbose("BytesReceived: " + hex(bytesReceived))
1528- LogWithPrefixIfVerbose("DHCP response:", HexDump(receiveBuffer, bytesReceived))
1529-
1530- # check transactionId, cookie, MAC address
1531- # cookie should never mismatch
1532- # transactionId and MAC address may mismatch if we see a response meant from another machine
1533-
1534- for offsets in [range(4, 4 + 4), range(0x1C, 0x1C + 6), range(0xEC, 0xEC + 4)]:
1535- for offset in offsets:
1536- sentByte = Ord(sendData[offset])
1537- receivedByte = Ord(receiveBuffer[offset])
1538- if sentByte != receivedByte:
1539- LogIfVerbose("HandleDhcpResponse: sent cookie:" + self.HexDump3(sendData, 0xEC, 4))
1540- LogIfVerbose("HandleDhcpResponse: rcvd cookie:" + self.HexDump3(receiveBuffer, 0xEC, 4))
1541- LogIfVerbose("HandleDhcpResponse: sent transactionID:" + self.HexDump3(sendData, 4, 4))
1542- LogIfVerbose("HandleDhcpResponse: rcvd transactionID:" + self.HexDump3(receiveBuffer, 4, 4))
1543- LogIfVerbose("HandleDhcpResponse: sent ClientHardwareAddress:" + self.HexDump3(sendData, 0x1C, 6))
1544- LogIfVerbose("HandleDhcpResponse: rcvd ClientHardwareAddress:" + self.HexDump3(receiveBuffer, 0x1C, 6))
1545- LogIfVerbose("HandleDhcpResponse: transactionId, cookie, or MAC address mismatch")
1546- return None
1547- endpoint = None
1548-
1549- #
1550- # Walk all the returned options, parsing out what we need, ignoring the others.
1551- # We need the custom option 245 to find the the endpoint we talk to,
1552- # as well as, to handle some Linux DHCP client incompatibilities,
1553- # options 3 for default gateway and 249 for routes. And 255 is end.
1554- #
1555-
1556- i = 0xF0 # offset to first option
1557- while i < bytesReceived:
1558- option = Ord(receiveBuffer[i])
1559- length = 0
1560- if (i + 1) < bytesReceived:
1561- length = Ord(receiveBuffer[i + 1])
1562- LogIfVerbose("DHCP option " + hex(option) + " at offset:" + hex(i) + " with length:" + hex(length))
1563- if option == 255:
1564- LogIfVerbose("DHCP packet ended at offset " + hex(i))
1565- break
1566- elif option == 249:
1567- # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
1568- LogIfVerbose("Routes at offset:" + hex(i) + " with length:" + hex(length))
1569- if length < 5:
1570- Error("Data too small for option " + option)
1571- j = i + 2
1572- while j < (i + length + 2):
1573- maskLengthBits = Ord(receiveBuffer[j])
1574- maskLengthBytes = (((maskLengthBits + 7) & ~7) >> 3)
1575- mask = 0xFFFFFFFF & (0xFFFFFFFF << (32 - maskLengthBits))
1576- j += 1
1577- net = self.UnpackBigEndian(receiveBuffer, j, maskLengthBytes)
1578- net <<= (32 - maskLengthBytes * 8)
1579- net &= mask
1580- j += maskLengthBytes
1581- gateway = self.UnpackBigEndian(receiveBuffer, j, 4)
1582- j += 4
1583- self.RouteAdd(net, mask, gateway)
1584- if j != (i + length + 2):
1585- Error("HandleDhcpResponse: Unable to parse routes")
1586- elif option == 3 or option == 245:
1587- if i + 5 < bytesReceived:
1588- if length != 4:
1589- Error("HandleDhcpResponse: Endpoint or Default Gateway not 4 bytes")
1590- return None
1591- gateway = self.UnpackBigEndian(receiveBuffer, i + 2, 4)
1592- IpAddress = self.IntegerToIpAddressV4String(gateway)
1593- if option == 3:
1594- self.RouteAdd(0, 0, gateway)
1595- name = "DefaultGateway"
1596- else:
1597- endpoint = IpAddress
1598- name = "Windows Azure wire protocol endpoint"
1599- LogIfVerbose(name + ": " + IpAddress + " at " + hex(i))
1600- else:
1601- Error("HandleDhcpResponse: Data too small for option " + option)
1602- else:
1603- LogIfVerbose("Skipping DHCP option " + hex(option) + " at " + hex(i) + " with length " + hex(length))
1604- i += length + 2
1605- return endpoint
1606-
1607- def DoDhcpWork(self):
1608- #
1609- # Discover the wire server via DHCP option 245.
1610- # And workaround incompatibility with Windows Azure DHCP servers.
1611- #
1612- ShortSleep = False # Sleep 1 second before retrying DHCP queries.
1613-
1614- if not IsWindows():
1615- Run("iptables -D INPUT -p udp --dport 68 -j ACCEPT")
1616- Run("iptables -I INPUT -p udp --dport 68 -j ACCEPT")
1617-
1618- sleepDurations = [0, 5, 10, 30, 60, 60, 60, 60]
1619- maxRetry = len(sleepDurations)
1620- lastTry = (maxRetry - 1)
1621- for retry in range(0, maxRetry):
1622- try:
1623- strRetry = str(retry)
1624- prefix = "DoDhcpWork: try=" + strRetry
1625- LogIfVerbose(prefix)
1626- sendData = self.BuildDhcpRequest()
1627- LogWithPrefixIfVerbose("DHCP request:", HexDump(sendData, len(sendData)))
1628- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
1629- sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
1630- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1631- if IsSuse():
1632- # This is required because sending after binding to 0.0.0.0 fails with
1633- # network unreachable when the default gateway is not set up.
1634- sock.bind((GetIpv4Address(), 68))
1635- else:
1636- sock.bind(("0.0.0.0", 68))
1637- sock.sendto(sendData, ("<broadcast>", 67))
1638- receiveBuffer = sock.recv(1024)
1639- sock.close()
1640- endpoint = self.HandleDhcpResponse(sendData, receiveBuffer)
1641- if endpoint == None:
1642- LogIfVerbose("DoDhcpWork: No endpoint found")
1643- if endpoint != None or retry == lastTry:
1644- if endpoint != None:
1645- self.SendData = sendData
1646- self.DhcpResponse = receiveBuffer
1647- if retry == lastTry:
1648- LogIfVerbose("DoDhcpWork: try=" + strRetry)
1649- return endpoint
1650- sleepDuration = [sleepDurations[retry % len(sleepDurations)], 1][ShortSleep]
1651- LogIfVerbose("DoDhcpWork: sleep=" + str(sleepDuration))
1652- time.sleep(sleepDuration)
1653- except Exception, e:
1654- ErrorWithPrefix(prefix, str(e))
1655- ErrorWithPrefix(prefix, traceback.format_exc())
1656- return None
1657-
1658- def UpdateAndPublishHostName(self, name):
1659- # Set hostname locally and publish to iDNS
1660- Log("Setting host name: " + name)
1661- UpdateAndPublishHostNameCommon(name)
1662- for ethernetInterface in PossibleEthernetInterfaces:
1663- if IsSuse():
1664- Run("ifrenew " + ethernetInterface)
1665- else:
1666- Run("ifdown " + ethernetInterface + " && ifup " + ethernetInterface)
1667- self.RestoreRoutes()
1668-
1669- def RestoreRoutes(self):
1670- if self.SendData != None and self.DhcpResponse != None:
1671- self.HandleDhcpResponse(self.SendData, self.DhcpResponse)
1672-
1673- def UpdateGoalState(self):
1674- goalStateXml = None
1675- maxRetry = 9
1676- log = NoLog
1677- for retry in range(1, maxRetry + 1):
1678- strRetry = str(retry)
1679- log("retry UpdateGoalState,retry=" + strRetry)
1680- goalStateXml = self.HttpGetWithHeaders("/machine/?comp=goalstate")
1681- if goalStateXml != None:
1682- break
1683- log = Log
1684- time.sleep(retry)
1685- if not goalStateXml:
1686- Error("UpdateGoalState failed.")
1687- return
1688- Log("Retrieved GoalState from Windows Azure Fabric.")
1689- self.GoalState = GoalState(self).Parse(goalStateXml)
1690- return self.GoalState
1691-
1692- def ReportReady(self):
1693- counter = (self.HealthReportCounter + 1) % 1000000
1694- self.HealthReportCounter = counter
1695- healthReport = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><Health xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GoalStateIncarnation>"
1696- + self.GoalState.Incarnation
1697- + "</GoalStateIncarnation><Container><ContainerId>"
1698- + self.GoalState.ContainerId
1699- + "</ContainerId><RoleInstanceList><Role><InstanceId>"
1700- + self.GoalState.RoleInstanceId
1701- + "</InstanceId><Health><State>Ready</State></Health></Role></RoleInstanceList></Container></Health>")
1702- a = self.HttpPost("/machine?comp=health", healthReport)
1703- if a != None:
1704- return a.getheader("x-ms-latest-goal-state-incarnation-number")
1705- return None
1706-
1707- def ReportNotReady(self, status, desc):
1708- healthReport = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><Health xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GoalStateIncarnation>"
1709- + self.GoalState.Incarnation
1710- + "</GoalStateIncarnation><Container><ContainerId>"
1711- + self.GoalState.ContainerId
1712- + "</ContainerId><RoleInstanceList><Role><InstanceId>"
1713- + self.GoalState.RoleInstanceId
1714- + "</InstanceId><Health><State>NotReady</State>"
1715- + "<Details><SubStatus>" + status + "</SubStatus><Description>" + desc + "</Description></Details>"
1716- + "</Health></Role></RoleInstanceList></Container></Health>")
1717- a = self.HttpPost("/machine?comp=health", healthReport)
1718- if a != None:
1719- return a.getheader("x-ms-latest-goal-state-incarnation-number")
1720- return None
1721-
1722- def ReportRoleProperties(self, thumbprint):
1723- roleProperties = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><RoleProperties><Container>"
1724- + "<ContainerId>" + self.GoalState.ContainerId + "</ContainerId>"
1725- + "<RoleInstances><RoleInstance>"
1726- + "<Id>" + self.GoalState.RoleInstanceId + "</Id>"
1727- + "<Properties><Property name=\"CertificateThumbprint\" value=\"" + thumbprint + "\" /></Properties>"
1728- + "</RoleInstance></RoleInstances></Container></RoleProperties>")
1729- a = self.HttpPost("/machine?comp=roleProperties", roleProperties)
1730- Log("Posted Role Properties. CertificateThumbprint=" + thumbprint)
1731- return a
1732-
1733- def LoadBalancerProbeServer_Shutdown(self):
1734- if self.LoadBalancerProbeServer != None:
1735- self.LoadBalancerProbeServer.shutdown()
1736- self.LoadBalancerProbeServer = None
1737-
1738- def GenerateTransportCert(self):
1739- Run(Openssl + " req -x509 -nodes -subj /CN=LinuxTransport -days 32768 -newkey rsa:2048 -keyout TransportPrivate.pem -out TransportCert.pem")
1740- cert = ""
1741- for line in GetFileContents("TransportCert.pem").split('\n'):
1742- if not "CERTIFICATE" in line:
1743- cert += line.rstrip()
1744- return cert
1745-
1746- def Provision(self):
1747- if IsWindows():
1748- Log("Skipping Provision on Windows")
1749- return
1750- enabled = Config.get("Provisioning.Enabled")
1751- if enabled != None and enabled.lower().startswith("n"):
1752- return
1753- Log("Provisioning image started.")
1754- type = Config.get("Provisioning.SshHostKeyPairType")
1755- if type == None:
1756- type = "rsa"
1757- regenerateKeys = Config.get("Provisioning.RegenerateSshHostKeyPair")
1758- if regenerateKeys == None or regenerateKeys.lower().startswith("y"):
1759- Run("rm -f /etc/ssh/ssh_host_*key*")
1760- Run("ssh-keygen -N '' -t " + type + " -f /etc/ssh/ssh_host_" + type + "_key")
1761- ReloadSshd()
1762- SetFileContents(LibDir + "/provisioned", "")
1763- dvd = "/dev/hdc"
1764- if os.path.exists("/dev/sr0"):
1765- dvd = "/dev/sr0"
1766- if Run("fdisk -l " + dvd + " | grep Disk"):
1767- return
1768- CreateDir("/mnt/cdrom/secure", "root", 0700)
1769- if Run("mount " + dvd + " /mnt/cdrom/secure"):
1770- Error("Unable to provision: Failed to mount DVD.")
1771- return "Failed to retrieve provisioning data (0x01)."
1772- if not os.path.isfile("/mnt/cdrom/secure/ovf-env.xml"):
1773- Error("Unable to provision: Missing ovf-env.xml on DVD.")
1774- return "Failed to retrieve provisioning data (0x02)."
1775- ovfxml = GetFileContents("/mnt/cdrom/secure/ovf-env.xml")
1776- SetFileContents("ovf-env.xml", re.sub("<UserPassword>.*?<", "<UserPassword>*<", ovfxml))
1777- Run("umount /mnt/cdrom/secure")
1778- error = None
1779- if ovfxml != None:
1780- Log("Provisioning image using OVF settings in the DVD.")
1781- ovfobj = OvfEnv().Parse(ovfxml)
1782- if ovfobj != None:
1783- error = ovfobj.Process()
1784- # This is done here because regenerated SSH host key pairs may be potentially overwritten when processing the ovfxml
1785- fingerprint = os.popen("ssh-keygen -lf /etc/ssh/ssh_host_" + type + "_key.pub").read().rstrip().split()[1].replace(':','')
1786- self.ReportRoleProperties(fingerprint)
1787- delRootPass = Config.get("Provisioning.DeleteRootPassword")
1788- if delRootPass != None and delRootPass.lower().startswith("y"):
1789- DeleteRootPassword()
1790- Log("Provisioning image completed.")
1791- return error
1792-
1793- def Run(self):
1794- if IsLinux():
1795- SetFileContents("/var/run/waagent.pid", str(os.getpid()) + "\n")
1796-
1797- if GetIpv4Address() == None:
1798- Log("Waiting for network.")
1799- while(GetIpv4Address() == None):
1800- time.sleep(10)
1801-
1802- Log("IPv4 address: " + GetIpv4Address())
1803- Log("MAC address: " + ":".join(["%02X" % Ord(a) for a in GetMacAddress()]))
1804-
1805- # Consume Entropy in ACPI table provided by Hyper-V
1806- try:
1807- SetFileContents("/dev/random", GetFileContents("/sys/firmware/acpi/tables/OEM0"))
1808- except:
1809- pass
1810-
1811- Log("Probing for Windows Azure environment.")
1812- self.Endpoint = self.DoDhcpWork()
1813-
1814- if self.Endpoint == None:
1815- Log("Windows Azure environment not detected.")
1816- while True:
1817- time.sleep(60)
1818-
1819- Log("Discovered Windows Azure endpoint: " + self.Endpoint)
1820- if not self.CheckVersions():
1821- Error("Agent.CheckVersions failed")
1822- sys.exit(1)
1823-
1824- self.EnvMonitor = EnvMonitor()
1825-
1826- # Set SCSI timeout on root device
1827- try:
1828- timeout = Config.get("OS.RootDeviceScsiTimeout")
1829- if timeout != None:
1830- SetFileContents("/sys/block/" + DeviceForIdePort(0) + "/device/timeout", timeout)
1831- except:
1832- pass
1833-
1834- global Openssl
1835- Openssl = Config.get("OS.OpensslPath")
1836- if Openssl == None:
1837- Openssl = "openssl"
1838-
1839- self.TransportCert = self.GenerateTransportCert()
1840-
1841- incarnation = None # goalStateIncarnationFromHealthReport
1842- currentPort = None # loadBalancerProbePort
1843- goalState = None # self.GoalState, instance of GoalState
1844- provisioned = os.path.exists(LibDir + "/provisioned")
1845- program = Config.get("Role.StateConsumer")
1846- provisionError = None
1847- while True:
1848- if (goalState == None) or (incarnation == None) or (goalState.Incarnation != incarnation):
1849- goalState = self.UpdateGoalState()
1850-
1851- if provisioned == False:
1852- self.ReportNotReady("Provisioning", "Starting")
1853-
1854- goalState.Process()
1855-
1856- if provisioned == False:
1857- provisionError = self.Provision()
1858- provisioned = True
1859-
1860- #
1861- # only one port supported
1862- # restart server if new port is different than old port
1863- # stop server if no longer a port
1864- #
1865- goalPort = goalState.LoadBalancerProbePort
1866- if currentPort != goalPort:
1867- self.LoadBalancerProbeServer_Shutdown()
1868- currentPort = goalPort
1869- if currentPort != None:
1870- self.LoadBalancerProbeServer = LoadBalancerProbeServer(currentPort)
1871-
1872- if program != None and DiskActivated == True:
1873- os.spawnl(os.P_NOWAIT, program, program, "Ready")
1874- program = None
1875-
1876- if goalState.ExpectedState == "Stopped":
1877- program = Config.get("Role.StateConsumer")
1878- if program != None:
1879- Run(program + " Shutdown")
1880- self.EnvMonitor.shutdown()
1881- self.LoadBalancerProbeServer_Shutdown()
1882- command = ["/sbin/shutdown -hP now", "shutdown /s /t 5"][IsWindows()]
1883- Run(command)
1884- return
1885-
1886- sleepToReduceAccessDenied = 3
1887- time.sleep(sleepToReduceAccessDenied)
1888- i = None
1889- if provisionError != None:
1890- i = self.ReportNotReady("ProvisioningFailed", provisionError)
1891- else:
1892- i = self.ReportReady()
1893- if i != None:
1894- incarnation = i
1895- time.sleep(25 - sleepToReduceAccessDenied)
1896-
1897-Init_Suse = """\
1898-#! /bin/sh
1899-
1900-### BEGIN INIT INFO
1901-# Provides: WindowsAzureLinuxAgent
1902-# Required-Start: $network sshd
1903-# Required-Stop: $network sshd
1904-# Default-Start: 3 5
1905-# Default-Stop: 0 1 2 6
1906-# Description: Start the WindowsAzureLinuxAgent
1907-### END INIT INFO
1908-
1909-WAZD_BIN=/usr/sbin/waagent
1910-test -x $WAZD_BIN || exit 5
1911-
1912-case "$1" in
1913- start)
1914- echo "Starting WindowsAzureLinuxAgent"
1915- ## Start daemon with startproc(8). If this fails
1916- ## the echo return value is set appropriate.
1917-
1918- startproc -f $WAZD_BIN -daemon
1919- exit $?
1920- ;;
1921- stop)
1922- echo "Shutting down WindowsAzureLinuxAgent"
1923- ## Stop daemon with killproc(8) and if this fails
1924- ## set echo the echo return value.
1925-
1926- killproc -p /var/run/waagent.pid $WAZD_BIN
1927- exit $?
1928- ;;
1929- try-restart)
1930- ## Stop the service and if this succeeds (i.e. the
1931- ## service was running before), start it again.
1932- $0 status >/dev/null && $0 restart
1933- ;;
1934- restart)
1935- ## Stop the service and regardless of whether it was
1936- ## running or not, start it again.
1937- $0 stop
1938- $0 start
1939- ;;
1940- force-reload|reload)
1941- ;;
1942- status)
1943- echo -n "Checking for service WindowsAzureLinuxAgent "
1944- ## Check status with checkproc(8), if process is running
1945- ## checkproc will return with exit status 0.
1946-
1947- checkproc -p $WAZD_PIDFILE $WAZD_BIN
1948- exit $?
1949- ;;
1950- probe)
1951- ;;
1952- *)
1953- echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}"
1954- exit 1
1955- ;;
1956-esac
1957-"""
1958-
1959-Init_RedHat = """\
1960-#!/bin/bash
1961-#
1962-# Init file for WindowsAzureLinuxAgent.
1963-#
1964-# chkconfig: 2345 60 80
1965-# description: WindowsAzureLinuxAgent
1966-#
1967-
1968-# source function library
1969-. /etc/rc.d/init.d/functions
1970-
1971-RETVAL=0
1972-FriendlyName="WindowsAzureLinuxAgent"
1973-WAZD_BIN=/usr/sbin/waagent
1974-
1975-start()
1976-{
1977- echo -n $"Starting $FriendlyName: "
1978- $WAZD_BIN -daemon &
1979-}
1980-
1981-stop()
1982-{
1983- echo -n $"Stopping $FriendlyName: "
1984- killproc -p /var/run/waagent.pid $WAZD_BIN
1985- RETVAL=$?
1986- echo
1987- return $RETVAL
1988-}
1989-
1990-case "$1" in
1991- start)
1992- start
1993- ;;
1994- stop)
1995- stop
1996- ;;
1997- restart)
1998- stop
1999- start
2000- ;;
2001- reload)
2002- ;;
2003- report)
2004- ;;
2005- status)
2006- status $WAZD_BIN
2007- RETVAL=$?
2008- ;;
2009- *)
2010- echo $"Usage: $0 {start|stop|restart|status}"
2011- RETVAL=1
2012-esac
2013-exit $RETVAL
2014-"""
2015-
2016-Init_Debian = """\
2017-#!/bin/sh
2018-### BEGIN INIT INFO
2019-# Provides: WindowsAzureLinuxAgent
2020-# Required-Start: $network $syslog
2021-# Required-Stop: $network $syslog
2022-# Should-Start: $network $syslog
2023-# Should-Stop: $network $syslog
2024-# Default-Start: 2 3 4 5
2025-# Default-Stop: 0 1 6
2026-# Short-Description: WindowsAzureLinuxAgent
2027-# Description: WindowsAzureLinuxAgent
2028-### END INIT INFO
2029-
2030-. /lib/lsb/init-functions
2031-
2032-OPTIONS="-daemon"
2033-WAZD_BIN=/usr/sbin/waagent
2034-WAZD_PID=/var/run/waagent.pid
2035-
2036-case "$1" in
2037- start)
2038- log_begin_msg "Starting WindowsAzureLinuxAgent..."
2039- pid=$( pidofproc $WAZD_BIN )
2040- if [ -n "$pid" ] ; then
2041- log_begin_msg "Already running."
2042- log_end_msg 0
2043- exit 0
2044- fi
2045- start-stop-daemon --start --quiet --oknodo --background --exec $WAZD_BIN -- $OPTIONS
2046- log_end_msg $?
2047- ;;
2048-
2049- stop)
2050- log_begin_msg "Stopping WindowsAzureLinuxAgent..."
2051- start-stop-daemon --stop --quiet --oknodo --pidfile $WAZD_PID
2052- ret=$?
2053- rm -f $WAZD_PID
2054- log_end_msg $ret
2055- ;;
2056- force-reload)
2057- $0 restart
2058- ;;
2059- restart)
2060- $0 stop
2061- $0 start
2062- ;;
2063- status)
2064- status_of_proc $WAZD_BIN && exit 0 || exit $?
2065- ;;
2066- *)
2067- log_success_msg "Usage: /etc/init.d/waagent {start|stop|force-reload|restart|status}"
2068- exit 1
2069- ;;
2070-esac
2071-
2072-exit 0
2073-"""
2074-
2075-WaagentConf = """\
2076-#
2077-# Windows Azure Linux Agent Configuration
2078-#
2079-
2080-Role.StateConsumer=None # Specified program is invoked with "Ready" or "Shutdown".
2081- # Shutdown will be initiated only after the program returns. Windows Azure will
2082- # power off the VM if shutdown is not completed within ?? minutes.
2083-Role.ConfigurationConsumer=None # Specified program is invoked with XML file argument specifying role configuration.
2084-Role.TopologyConsumer=None # Specified program is invoked with XML file argument specifying role topology.
2085-
2086-Provisioning.Enabled=y #
2087-Provisioning.DeleteRootPassword=y # Password authentication for root account will be unavailable.
2088-Provisioning.RegenerateSshHostKeyPair=y # Generate fresh host key pair.
2089-Provisioning.SshHostKeyPairType=rsa # Supported values are "rsa", "dsa" and "ecdsa".
2090-Provisioning.MonitorHostName=y # Monitor host name changes and publish changes via DHCP requests.
2091-
2092-ResourceDisk.Format=y # Format if unformatted. If 'n', resource disk will not be mounted.
2093-ResourceDisk.Filesystem=ext4 #
2094-ResourceDisk.MountPoint=/mnt/resource #
2095-ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
2096-ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
2097-
2098-LBProbeResponder=y # Respond to load balancer probes if requested by Windows Azure.
2099-
2100-Logs.Verbose=n #
2101-
2102-OS.RootDeviceScsiTimeout=300 # Root device timeout in seconds.
2103-OS.OpensslPath=None # If "None", the system default version is used.
2104-"""
2105-
2106-WaagentLogrotate = """\
2107-/var/log/waagent.log {
2108- monthly
2109- rotate 6
2110- notifempty
2111- missingok
2112-}
2113-"""
2114-
2115-def AddToLinuxKernelCmdline(options):
2116- if os.path.isfile("/boot/grub/menu.lst"):
2117- Run("sed -i --follow-symlinks '/kernel/s|$| " + options + " |' /boot/grub/menu.lst")
2118- filepath = "/etc/default/grub"
2119- if os.path.isfile(filepath):
2120- filecontents = GetFileContents(filepath).split('\n')
2121- current = filter(lambda a: a.startswith("GRUB_CMDLINE_LINUX"), filecontents)
2122- ReplaceFileContentsAtomic(filepath,
2123- "\n".join(filter(lambda a: not a.startswith("GRUB_CMDLINE_LINUX"), filecontents))
2124- + current[0][:-1] + " " + options + "\"\n")
2125- Run("update-grub")
2126-
2127-def ApplyVNUMAWorkaround():
2128- VersionParts = platform.release().replace('-', '.').split('.')
2129- if int(VersionParts[0]) > 2:
2130- return
2131- if int(VersionParts[1]) > 6:
2132- return
2133- if int(VersionParts[2]) > 37:
2134- return
2135- AddToLinuxKernelCmdline("numa=off")
2136- # TODO: This is not ideal for offline installation.
2137- print("Your kernel version " + platform.release() + " has a NUMA-related bug: NUMA has been disabled.")
2138-
2139-def RevertVNUMAWorkaround():
2140- print("Automatic reverting of GRUB configuration is not yet supported. Please edit by hand.")
2141-
2142-def Install():
2143- if IsWindows():
2144- print("ERROR: -install invalid for Windows.")
2145- return 1
2146- os.chmod(sys.argv[0], 0755)
2147- SwitchCwd()
2148- requiredDeps = [ "/sbin/route", "/sbin/shutdown" ]
2149- if IsDebian():
2150- requiredDeps += [ "/usr/sbin/update-rc.d" ]
2151- if IsSuse():
2152- requiredDeps += [ "/sbin/insserv" ]
2153- for a in requiredDeps:
2154- if not os.path.isfile(a):
2155- Error("Missing required dependency: " + a)
2156- return 1
2157- missing = False
2158- for a in [ "ssh-keygen", "useradd", "openssl", "sfdisk",
2159- "fdisk", "mkfs", "chpasswd", "sed", "grep", "sudo" ]:
2160- if Run("which " + a + " > /dev/null 2>&1"):
2161- Warn("Missing dependency: " + a)
2162- missing = True
2163- if missing == True:
2164- Warn("Please resolve missing dependencies listed for full functionality.")
2165- if UsesRpm():
2166- if not Run("rpm --quiet -q NetworkManager"):
2167- Error(GuestAgentLongName + " is not compatible with NetworkManager.")
2168- return 1
2169- if Run("rpm --quiet -q python-pyasn1"):
2170- Error(GuestAgentLongName + " requires python-pyasn1.")
2171- return 1
2172- if UsesDpkg() and Run("dpkg -l network-manager | grep -q ^un"):
2173- Error(GuestAgentLongName + " is not compatible with network-manager.")
2174- return 1
2175- for a in RulesFiles:
2176- if os.path.isfile(a):
2177- if os.path.isfile(GetLastPathElement(a)):
2178- os.remove(GetLastPathElement(a))
2179- shutil.move(a, ".")
2180- Warn("Moved " + a + " -> " + LibDir + "/" + GetLastPathElement(a) )
2181- filename = "waagent"
2182- filepath = "/etc/init.d/" + filename
2183- distro = IsRedHat() + IsDebian() * 2 + IsSuse() * 3
2184- if distro == 0:
2185- Error("Unable to detect Linux Distribution.")
2186- return 1
2187- init = [[Init_RedHat, "chkconfig --add " + filename],
2188- [Init_Debian, "update-rc.d " + filename + " defaults"],
2189- [Init_Suse, "insserv " + filename]][distro - 1]
2190- SetFileContents(filepath, init[0])
2191- os.chmod(filepath, 0755)
2192- Run(init[1])
2193- if os.path.isfile("/etc/waagent.conf"):
2194- try:
2195- os.remove("/etc/waagent.conf.old")
2196- except:
2197- pass
2198- try:
2199- os.rename("/etc/waagent.conf", "/etc/waagent.conf.old")
2200- Warn("Existing /etc/waagent.conf has been renamed to /etc/waagent.conf.old")
2201- except:
2202- pass
2203- SetFileContents("/etc/waagent.conf", WaagentConf)
2204- SetFileContents("/etc/logrotate.d/waagent", WaagentLogrotate)
2205- filepath = "/etc/ssh/sshd_config"
2206- ReplaceFileContentsAtomic(filepath, "\n".join(filter(lambda a: not
2207- a.startswith("ClientAliveInterval"),
2208- GetFileContents(filepath).split('\n'))) + "ClientAliveInterval 180\n")
2209- Log("Configured SSH client probing to keep connections alive.")
2210- ApplyVNUMAWorkaround()
2211- return 0
2212-
2213-def Uninstall():
2214- if IsWindows():
2215- print("ERROR: -uninstall invalid for windows, see waagent_service.exe")
2216- return 1
2217- SwitchCwd()
2218- for a in RulesFiles:
2219- if os.path.isfile(GetLastPathElement(a)):
2220- try:
2221- shutil.move(GetLastPathElement(a), a)
2222- Warn("Moved " + LibDir + "/" + GetLastPathElement(a) + " -> " + a )
2223- except:
2224- pass
2225- filename = "waagent"
2226- a = IsRedHat() + IsDebian() * 2 + IsSuse() * 3
2227- if a == 0:
2228- Error("Unable to detect Linux Distribution.")
2229- return 1
2230- Run("service " + filename + " stop")
2231- cmd = ["chkconfig --del " + filename,
2232- "update-rc.d -f " + filename + " remove",
2233- "insserv -r " + filename][a - 1]
2234- Run(cmd)
2235- for f in os.listdir(LibDir) + ["/etc/init.d/" + filename, "/etc/waagent.conf", "/etc/logrotate.d/waagent", "/etc/sudoers.d/waagent"]:
2236- try:
2237- os.remove(f)
2238- except:
2239- pass
2240- RevertVNUMAWorkaround()
2241- return 0
2242-
2243-def DeleteRootPassword():
2244- SetFileContents("/etc/shadow-temp", "")
2245- os.chmod("/etc/shadow-temp", 0000)
2246- Run("(echo root:*LOCK*:14600:::::: && grep -v ^root /etc/shadow ) > /etc/shadow-temp")
2247- Run("mv -f /etc/shadow-temp /etc/shadow")
2248- Log("Root password deleted.")
2249-
2250-def Deprovision(force, deluser):
2251- if IsWindows():
2252- Run(os.environ["windir"] + "\\system32\\sysprep\\sysprep.exe /generalize")
2253- return 0
2254-
2255- SwitchCwd()
2256- ovfxml = GetFileContents("ovf-env.xml")
2257- ovfobj = None
2258- if ovfxml != None:
2259- ovfobj = OvfEnv().Parse(ovfxml)
2260-
2261- print("WARNING! The waagent service will be stopped.")
2262- print("WARNING! All SSH host key pairs will be deleted.")
2263- print("WARNING! Nameserver configuration in /etc/resolv.conf will be deleted.")
2264- print("WARNING! Cached DHCP leases will be deleted.")
2265-
2266- delRootPass = Config.get("Provisioning.DeleteRootPassword")
2267- if delRootPass != None and delRootPass.lower().startswith("y"):
2268- print("WARNING! root password will be disabled. You will not be able to login as root.")
2269-
2270- if ovfobj != None and deluser == True:
2271- print("WARNING! " + ovfobj.UserName + " account and entire home directory will be deleted.")
2272-
2273- if force == False and not raw_input('Do you want to proceed (y/n)? ').startswith('y'):
2274- return 1
2275-
2276- Run("service waagent stop")
2277-
2278- if deluser == True:
2279- DeleteAccount(ovfobj.UserName)
2280-
2281- # Remove SSH host keys
2282- regenerateKeys = Config.get("Provisioning.RegenerateSshHostKeyPair")
2283- if regenerateKeys == None or regenerateKeys.lower().startswith("y"):
2284- Run("rm -f /etc/ssh/ssh_host_*key*")
2285-
2286- # Remove root password
2287- if delRootPass != None and delRootPass.lower().startswith("y"):
2288- DeleteRootPassword()
2289-
2290- # Remove distribution specific networking configuration
2291-
2292- UpdateAndPublishHostNameCommon("localhost.localdomain")
2293-
2294- # RedHat, Suse, Debian
2295- for a in VarLibDhcpDirectories:
2296- Run("rm -f " + a + "/*")
2297-
2298- # Clear LibDir, remove nameserver and root bash history
2299- for f in os.listdir(LibDir) + ["/etc/resolv.conf", "/root/.bash_history", "/var/log/waagent.log"]:
2300- try:
2301- os.remove(f)
2302- except:
2303- pass
2304-
2305- return 0
2306-
2307-def SwitchCwd():
2308- if not IsWindows():
2309- CreateDir(LibDir, "root", 0700)
2310- os.chdir(LibDir)
2311-
2312-def Usage():
2313- print("usage: " + sys.argv[0] + " [-verbose] [-force] [-help|-install|-uninstall|-deprovision[+user]|-version|-serialconsole|-daemon]")
2314- return 0
2315-
2316-if GuestAgentVersion == "":
2317- print("WARNING! This is a non-standard agent that does not include a valid version string.")
2318-if IsLinux() and not DetectLinuxDistro():
2319- print("WARNING! Unable to detect Linux distribution. Some functionality may be broken.")
2320-
2321-if len(sys.argv) == 1:
2322- sys.exit(Usage())
2323-
2324-args = []
2325-force = False
2326-for a in sys.argv[1:]:
2327- if re.match("^([-/]*)(help|usage|\?)", a):
2328- sys.exit(Usage())
2329- elif re.match("^([-/]*)verbose", a):
2330- Verbose = True
2331- elif re.match("^([-/]*)force", a):
2332- force = True
2333- elif re.match("^([-/]*)(setup|install)", a):
2334- sys.exit(Install())
2335- elif re.match("^([-/]*)(uninstall)", a):
2336- sys.exit(Uninstall())
2337- else:
2338- args.append(a)
2339-
2340-Config = ConfigurationProvider()
2341-
2342-verbose = Config.get("Logs.Verbose")
2343-if verbose != None and verbose.lower().startswith("y"):
2344- Verbose = True
2345-
2346-daemon = False
2347-for a in args:
2348- if re.match("^([-/]*)deprovision\+user", a):
2349- sys.exit(Deprovision(force, True))
2350- elif re.match("^([-/]*)deprovision", a):
2351- sys.exit(Deprovision(force, False))
2352- elif re.match("^([-/]*)daemon", a):
2353- daemon = True
2354- elif re.match("^([-/]*)version", a):
2355- print(GuestAgentVersion + " running on " + LinuxDistro)
2356- sys.exit(0)
2357- elif re.match("^([-/]*)serialconsole", a):
2358- AddToLinuxKernelCmdline("console=ttyS0 earlyprintk=ttyS0")
2359- Log("Configured kernel to use ttyS0 as the boot console.")
2360- sys.exit(0)
2361- else:
2362- print("Invalid command line parameter:" + a)
2363- sys.exit(1)
2364-
2365-if daemon == False:
2366- sys.exit(Usage())
2367-
2368-try:
2369- SwitchCwd()
2370- Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
2371- if IsLinux():
2372- Log("Linux Distribution Detected : " + LinuxDistro)
2373- WaAgent = Agent()
2374- WaAgent.Run()
2375-except Exception, e:
2376- Error(traceback.format_exc())
2377- Error("Exception: " + str(e))
2378- sys.exit(1)
2379
2380=== added directory '.pc/000_use_package_upstart.patch'
2381=== added file '.pc/000_use_package_upstart.patch/waagent'
2382--- .pc/000_use_package_upstart.patch/waagent 1970-01-01 00:00:00 +0000
2383+++ .pc/000_use_package_upstart.patch/waagent 2012-12-13 16:29:21 +0000
2384@@ -0,0 +1,2473 @@
2385+#!/usr/bin/python
2386+#
2387+# Windows Azure Linux Agent
2388+#
2389+# Copyright 2012 Microsoft Corporation
2390+#
2391+# Licensed under the Apache License, Version 2.0 (the "License");
2392+# you may not use this file except in compliance with the License.
2393+# You may obtain a copy of the License at
2394+#
2395+# http://www.apache.org/licenses/LICENSE-2.0
2396+#
2397+# Unless required by applicable law or agreed to in writing, software
2398+# distributed under the License is distributed on an "AS IS" BASIS,
2399+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2400+# See the License for the specific language governing permissions and
2401+# limitations under the License.
2402+#
2403+# Requires Python 2.4+ and Openssl 1.0+
2404+#
2405+# Implements parts of RFC 2131, 1541, 1497 and
2406+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
2407+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
2408+#
2409+
2410+import array
2411+import base64
2412+import httplib
2413+import os
2414+import os.path
2415+import platform
2416+import pwd
2417+import re
2418+import shutil
2419+import socket
2420+import SocketServer
2421+import struct
2422+import subprocess
2423+import sys
2424+import tempfile
2425+import textwrap
2426+import threading
2427+import time
2428+import traceback
2429+import xml.dom.minidom
2430+import commands
2431+
2432+GuestAgentName = "WALinuxAgent"
2433+GuestAgentLongName = "Windows Azure Linux Agent"
2434+GuestAgentVersion = "WALinuxAgent-1.2"
2435+ProtocolVersion = "2011-12-31"
2436+
2437+Config = None
2438+LinuxDistro = "UNKNOWN"
2439+Verbose = False
2440+WaAgent = None
2441+DiskActivated = False
2442+Openssl = "openssl"
2443+Children = []
2444+
2445+PossibleEthernetInterfaces = ["seth0", "seth1", "eth0", "eth1"]
2446+RulesFiles = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
2447+ "/etc/udev/rules.d/70-persistent-net.rules" ]
2448+VarLibDhcpDirectories = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"]
2449+EtcDhcpClientConfFiles = ["/etc/dhcp/dhclient.conf", "/etc/dhcp3/dhclient.conf"]
2450+LibDir = "/var/lib/waagent"
2451+
2452+# This lets us index into a string or an array of integers transparently.
2453+def Ord(a):
2454+ if type(a) == type("a"):
2455+ a = ord(a)
2456+ return a
2457+
2458+def IsWindows():
2459+ return (platform.uname()[0] == "Windows")
2460+
2461+def IsLinux():
2462+ return (platform.uname()[0] == "Linux")
2463+
2464+def DetectLinuxDistro():
2465+ global LinuxDistro
2466+ if os.path.isfile("/etc/redhat-release"):
2467+ LinuxDistro = "RedHat"
2468+ return True
2469+ if os.path.isfile("/etc/lsb-release") and "Ubuntu" in GetFileContents("/etc/lsb-release"):
2470+ LinuxDistro = "Ubuntu"
2471+ return True
2472+ if os.path.isfile("/etc/debian_version"):
2473+ LinuxDistro = "Debian"
2474+ return True
2475+ if os.path.isfile("/etc/SuSE-release"):
2476+ LinuxDistro = "Suse"
2477+ return True
2478+ return False
2479+
2480+def IsRedHat():
2481+ return "RedHat" in LinuxDistro
2482+
2483+def IsUbuntu():
2484+ return "Ubuntu" in LinuxDistro
2485+
2486+def IsDebian():
2487+ return IsUbuntu() or "Debian" in LinuxDistro
2488+
2489+def IsSuse():
2490+ return "Suse" in LinuxDistro
2491+
2492+def UsesRpm():
2493+ return IsRedHat() or IsSuse()
2494+
2495+def UsesDpkg():
2496+ return IsDebian()
2497+
2498+def GetLastPathElement(path):
2499+ return path.rsplit('/', 1)[1]
2500+
2501+def GetFileContents(filepath):
2502+ file = None
2503+ try:
2504+ file = open(filepath)
2505+ except:
2506+ return None
2507+ if file == None:
2508+ return None
2509+ try:
2510+ return file.read()
2511+ finally:
2512+ file.close()
2513+
2514+def SetFileContents(filepath, contents):
2515+ file = open(filepath, "w")
2516+ try:
2517+ file.write(contents)
2518+ finally:
2519+ file.close()
2520+
2521+def AppendFileContents(filepath, contents):
2522+ file = open(filepath, "a")
2523+ try:
2524+ file.write(contents)
2525+ finally:
2526+ file.close()
2527+
2528+def ReplaceFileContentsAtomic(filepath, contents):
2529+ handle, temp = tempfile.mkstemp(dir = os.path.dirname(filepath))
2530+ try:
2531+ os.write(handle, contents)
2532+ finally:
2533+ os.close(handle)
2534+ try:
2535+ os.rename(temp, filepath)
2536+ return
2537+ except:
2538+ pass
2539+ os.remove(filepath)
2540+ os.rename(temp, filepath)
2541+
2542+def GetLineStartingWith(prefix, filepath):
2543+ for line in GetFileContents(filepath).split('\n'):
2544+ if line.startswith(prefix):
2545+ return line
2546+ return None
2547+
2548+def Run(a):
2549+ LogIfVerbose(a)
2550+ return os.system(a)
2551+
2552+def RunSafe(cmd):
2553+ LogIfVerbose(cmd)
2554+ # for python2.1 double try, in order to use a finally...
2555+ try:
2556+ try:
2557+ (exit_status,output) = commands.getstatusoutput(cmd)
2558+ except OSError,e : # just catch the exception and proceed
2559+ LogIfVerbose( ("OSError " + str(e) + " caught") )
2560+ return exit_status,output
2561+ else:
2562+ return exit_status,output
2563+ finally:
2564+ pass
2565+
2566+def GetNodeTextData(a):
2567+ for b in a.childNodes:
2568+ if b.nodeType == b.TEXT_NODE:
2569+ return b.data
2570+
2571+def GetHome():
2572+ home = None
2573+ try:
2574+ home = GetLineStartingWith("HOME", "/etc/default/useradd").split('=')[1].strip()
2575+ except:
2576+ pass
2577+ if (home == None) or (home.startswith("/") == False):
2578+ home = "/home"
2579+ return home
2580+
2581+def ChangeOwner(filepath, user):
2582+ p = None
2583+ try:
2584+ p = pwd.getpwnam(user)
2585+ except:
2586+ pass
2587+ if p != None:
2588+ os.chown(filepath, p[2], p[3])
2589+
2590+def CreateDir(dirpath, user, mode):
2591+ try:
2592+ os.makedirs(dirpath, mode)
2593+ except:
2594+ pass
2595+ ChangeOwner(dirpath, user)
2596+
2597+def CreateAccount(user, password, expiration, thumbprint):
2598+ if IsWindows():
2599+ Log("Skipping CreateAccount on Windows")
2600+ return None
2601+ userentry = None
2602+ try:
2603+ userentry = pwd.getpwnam(user)
2604+ except:
2605+ pass
2606+ uidmin = None
2607+ try:
2608+ uidmin = int(GetLineStartingWith("UID_MIN", "/etc/login.defs").split()[1])
2609+ except:
2610+ pass
2611+ if uidmin == None:
2612+ uidmin = 100
2613+ if userentry != None and userentry[2] < uidmin:
2614+ Error("CreateAccount: " + user + " is a system user. Will not set password.")
2615+ return "Failed to set password for system user: " + user + " (0x06)."
2616+ if userentry == None:
2617+ command = "useradd -m " + user
2618+ if expiration != None:
2619+ command += " -e " + expiration.split('.')[0]
2620+ if Run(command):
2621+ Error("Failed to create user account: " + user)
2622+ return "Failed to create user account: " + user + " (0x07)."
2623+ else:
2624+ Log("CreateAccount: " + user + " already exists. Will update password.")
2625+ if password != None:
2626+ os.popen("chpasswd", "w").write(user + ":" + password + "\n")
2627+ try:
2628+ if password == None:
2629+ SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n")
2630+ else:
2631+ SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) ALL\n")
2632+ os.chmod("/etc/sudoers.d/waagent", 0440)
2633+ except:
2634+ Error("CreateAccount: Failed to configure sudo access for user.")
2635+ return "Failed to configure sudo privileges (0x08)."
2636+ home = GetHome()
2637+ if thumbprint != None:
2638+ dir = home + "/" + user + "/.ssh"
2639+ CreateDir(dir, user, 0700)
2640+ pub = dir + "/id_rsa.pub"
2641+ prv = dir + "/id_rsa"
2642+ Run("ssh-keygen -y -f " + thumbprint + ".prv > " + pub)
2643+ SetFileContents(prv, GetFileContents(thumbprint + ".prv"))
2644+ for f in [pub, prv]:
2645+ os.chmod(f, 0600)
2646+ ChangeOwner(f, user)
2647+ SetFileContents(dir + "/authorized_keys", GetFileContents(pub))
2648+ ChangeOwner(dir + "/authorized_keys", user)
2649+ Log("Created user account: " + user)
2650+ return None
2651+
2652+def DeleteAccount(user):
2653+ if IsWindows():
2654+ Log("Skipping DeleteAccount on Windows")
2655+ return
2656+ userentry = None
2657+ try:
2658+ userentry = pwd.getpwnam(user)
2659+ except:
2660+ pass
2661+ if userentry == None:
2662+ Error("DeleteAccount: " + user + " not found.")
2663+ return
2664+ uidmin = None
2665+ try:
2666+ uidmin = int(GetLineStartingWith("UID_MIN", "/etc/login.defs").split()[1])
2667+ except:
2668+ pass
2669+ if uidmin == None:
2670+ uidmin = 100
2671+ if userentry[2] < uidmin:
2672+ Error("DeleteAccount: " + user + " is a system user. Will not delete account.")
2673+ return
2674+ Run("> /var/run/utmp") #Delete utmp to prevent error if we are the 'user' deleted
2675+ Run("userdel -f -r " + user)
2676+ try:
2677+ os.remove("/etc/sudoers.d/waagent")
2678+ except:
2679+ pass
2680+ return
2681+
2682+def ReloadSshd():
2683+ name = None
2684+ if IsRedHat() or IsSuse():
2685+ name = "sshd"
2686+ if IsDebian():
2687+ name = "ssh"
2688+ if name == None:
2689+ return
2690+ if not Run("service " + name + " status | grep running"):
2691+ Run("service " + name + " reload")
2692+
2693+def IsInRangeInclusive(a, low, high):
2694+ return (a >= low and a <= high)
2695+
2696+def IsPrintable(ch):
2697+ return IsInRangeInclusive(ch, Ord('A'), Ord('Z')) or IsInRangeInclusive(ch, Ord('a'), Ord('z')) or IsInRangeInclusive(ch, Ord('0'), Ord('9'))
2698+
2699+def HexDump(buffer, size):
2700+ if size < 0:
2701+ size = len(buffer)
2702+ result = ""
2703+ for i in range(0, size):
2704+ if (i % 16) == 0:
2705+ result += "%06X: " % i
2706+ byte = struct.unpack("B", buffer[i])[0]
2707+ result += "%02X " % byte
2708+ if (i & 15) == 7:
2709+ result += " "
2710+ if ((i + 1) % 16) == 0 or (i + 1) == size:
2711+ j = i
2712+ while ((j + 1) % 16) != 0:
2713+ result += " "
2714+ if (j & 7) == 7:
2715+ result += " "
2716+ j += 1
2717+ result += " "
2718+ for j in range(i - (i % 16), i + 1):
2719+ byte = struct.unpack("B", buffer[j])[0]
2720+ k = '.'
2721+ if IsPrintable(byte):
2722+ k = chr(byte)
2723+ result += k
2724+ if (i + 1) != size:
2725+ result += "\n"
2726+ return result
2727+
2728+def ThrottleLog(counter):
2729+ # Log everything up to 10, every 10 up to 100, then every 100.
2730+ return (counter < 10) or ((counter < 100) and ((counter % 10) == 0)) or ((counter % 100) == 0)
2731+
2732+def Logger():
2733+ class T(object):
2734+ def __init__(self):
2735+ self.File = None
2736+
2737+ self = T()
2738+
2739+ def LogToFile(message):
2740+ FilePath = ["/var/log/waagent.log", "waagent.log"][IsWindows()]
2741+ if not os.path.isfile(FilePath) and self.File != None:
2742+ self.File.close()
2743+ self.File = None
2744+ if self.File == None:
2745+ self.File = open(FilePath, "a")
2746+ self.File.write(message + "\n")
2747+ self.File.flush()
2748+
2749+ def Log(message):
2750+ LogWithPrefix("", message)
2751+
2752+ def LogWithPrefix(prefix, message):
2753+ t = time.localtime()
2754+ t = "%04u/%02u/%02u %02u:%02u:%02u " % (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
2755+ t += prefix
2756+ for line in message.split('\n'):
2757+ line = t + line
2758+ print(line)
2759+ LogToFile(line)
2760+
2761+ return Log, LogWithPrefix
2762+
2763+Log, LogWithPrefix = Logger()
2764+
2765+def NoLog(message):
2766+ pass
2767+
2768+def LogIfVerbose(message):
2769+ if Verbose == True:
2770+ Log(message)
2771+
2772+def LogWithPrefixIfVerbose(prefix, message):
2773+ if Verbose == True:
2774+ LogWithPrefix(prefix, message)
2775+
2776+def Warn(message):
2777+ LogWithPrefix("WARNING:", message)
2778+
2779+def Error(message):
2780+ LogWithPrefix("ERROR:", message)
2781+
2782+def ErrorWithPrefix(prefix, message):
2783+ LogWithPrefix("ERROR:" + prefix, message)
2784+
2785+def Linux_ioctl_GetIpv4Address(ifname):
2786+ import fcntl
2787+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2788+ return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
2789+
2790+def Linux_ioctl_GetInterfaceMac(ifname):
2791+ import fcntl
2792+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2793+ info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15]))
2794+ return ''.join(['%02X' % Ord(char) for char in info[18:24]])
2795+
2796+def GetIpv4Address():
2797+ if IsLinux():
2798+ for ifname in PossibleEthernetInterfaces:
2799+ try:
2800+ return Linux_ioctl_GetIpv4Address(ifname)
2801+ except IOError, e:
2802+ pass
2803+ else:
2804+ try:
2805+ return socket.gethostbyname(socket.gethostname())
2806+ except Exception, e:
2807+ ErrorWithPrefix("GetIpv4Address:", str(e))
2808+ ErrorWithPrefix("GetIpv4Address:", traceback.format_exc())
2809+
2810+def HexStringToByteArray(a):
2811+ b = ""
2812+ for c in range(0, len(a) / 2):
2813+ b += struct.pack("B", int(a[c * 2:c * 2 + 2], 16))
2814+ return b
2815+
2816+def GetMacAddress():
2817+ if IsWindows():
2818+ # Windows: Physical Address. . . . . . . . . : 00-15-17-79-00-7F\n
2819+ a = "ipconfig /all | findstr /c:\"Physical Address\" | findstr /v \"00-00-00-00-00-00-00\""
2820+ a = os.popen(a).read()
2821+ a = re.sub("\s+$", "", a)
2822+ a = re.sub(".+ ", "", a)
2823+ a = re.sub(":", "", a)
2824+ a = re.sub("-", "", a)
2825+ else:
2826+ for ifname in PossibleEthernetInterfaces:
2827+ try:
2828+ a = Linux_ioctl_GetInterfaceMac(ifname)
2829+ break
2830+ except IOError, e:
2831+ pass
2832+ return HexStringToByteArray(a)
2833+
2834+def DeviceForIdePort(n):
2835+ if n > 3:
2836+ return None
2837+ g0 = "00000000"
2838+ if n > 1:
2839+ g0 = "00000001"
2840+ n = n - 2
2841+ device = None
2842+ path = "/sys/bus/vmbus/devices/"
2843+ for vmbus in os.listdir(path):
2844+ guid = GetFileContents(path + vmbus + "/device_id").lstrip('{').split('-')
2845+ if guid[0] == g0 and guid[1] == "000" + str(n):
2846+ for root, dirs, files in os.walk(path + vmbus):
2847+ if root.endswith("/block"):
2848+ device = dirs[0]
2849+ break
2850+ break
2851+ return device
2852+
2853+class Util(object):
2854+ def _HttpGet(self, url, headers):
2855+ LogIfVerbose("HttpGet(" + url + ")")
2856+ maxRetry = 2
2857+ if url.startswith("http://"):
2858+ url = url[7:]
2859+ url = url[url.index("/"):]
2860+ for retry in range(0, maxRetry + 1):
2861+ strRetry = str(retry)
2862+ log = [NoLog, Log][retry > 0]
2863+ log("retry HttpGet(" + url + "),retry=" + strRetry)
2864+ response = None
2865+ strStatus = "None"
2866+ try:
2867+ httpConnection = httplib.HTTPConnection(self.Endpoint)
2868+ if headers == None:
2869+ request = httpConnection.request("GET", url)
2870+ else:
2871+ request = httpConnection.request("GET", url, None, headers)
2872+ response = httpConnection.getresponse()
2873+ strStatus = str(response.status)
2874+ except:
2875+ pass
2876+ log("response HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2877+ if response == None or response.status != httplib.OK:
2878+ Error("HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2879+ if retry == maxRetry:
2880+ Log("return HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2881+ return None
2882+ else:
2883+ Log("sleep 10 seconds HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2884+ time.sleep(10)
2885+ else:
2886+ log("return HttpGet(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2887+ return response.read()
2888+
2889+ def HttpGetWithoutHeaders(self, url):
2890+ return self._HttpGet(url, None)
2891+
2892+ def HttpGetWithHeaders(self, url):
2893+ return self._HttpGet(url, {"x-ms-agent-name": GuestAgentName, "x-ms-version": ProtocolVersion})
2894+
2895+ def HttpSecureGetWithHeaders(self, url, transportCert):
2896+ return self._HttpGet(url, {"x-ms-agent-name": GuestAgentName,
2897+ "x-ms-version": ProtocolVersion,
2898+ "x-ms-cipher-name": "DES_EDE3_CBC",
2899+ "x-ms-guest-agent-public-x509-cert": transportCert})
2900+
2901+ def HttpPost(self, url, data):
2902+ LogIfVerbose("HttpPost(" + url + ")")
2903+ maxRetry = 2
2904+ for retry in range(0, maxRetry + 1):
2905+ strRetry = str(retry)
2906+ log = [NoLog, Log][retry > 0]
2907+ log("retry HttpPost(" + url + "),retry=" + strRetry)
2908+ response = None
2909+ strStatus = "None"
2910+ try:
2911+ httpConnection = httplib.HTTPConnection(self.Endpoint)
2912+ request = httpConnection.request("POST", url, data, {"x-ms-agent-name": GuestAgentName,
2913+ "Content-Type": "text/xml; charset=utf-8",
2914+ "x-ms-version": ProtocolVersion})
2915+ response = httpConnection.getresponse()
2916+ strStatus = str(response.status)
2917+ except:
2918+ pass
2919+ log("response HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2920+ if response == None or (response.status != httplib.OK and response.status != httplib.ACCEPTED):
2921+ Error("HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2922+ if retry == maxRetry:
2923+ Log("return HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2924+ return None
2925+ else:
2926+ Log("sleep 10 seconds HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2927+ time.sleep(10)
2928+ else:
2929+ log("return HttpPost(" + url + "),retry=" + strRetry + ",status=" + strStatus)
2930+ return response
2931+
2932+def LoadBalancerProbeServer(port):
2933+
2934+ class T(object):
2935+ def __init__(self, ip, port):
2936+ if port == None or ip == None :
2937+ return
2938+ self.ProbeCounter = 0
2939+ self.server = SocketServer.TCPServer((ip, port), TCPHandler)
2940+ self.server_thread = threading.Thread(target = self.server.serve_forever)
2941+ self.server_thread.setDaemon(True)
2942+ self.server_thread.start()
2943+
2944+ def shutdown(self):
2945+ self.server.shutdown()
2946+
2947+ class TCPHandler(SocketServer.BaseRequestHandler):
2948+ def GetHttpDateTimeNow(self):
2949+ # Date: Fri, 25 Mar 2011 04:53:10 GMT
2950+ return time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
2951+
2952+ def handle(self):
2953+ context.ProbeCounter = (context.ProbeCounter + 1) % 1000000
2954+ log = [NoLog, LogIfVerbose][ThrottleLog(context.ProbeCounter)]
2955+ strCounter = str(context.ProbeCounter)
2956+ if context.ProbeCounter == 1:
2957+ Log("Receiving LB probes.")
2958+ log("Received LB probe # " + strCounter)
2959+ self.request.recv(1024)
2960+ self.request.send("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/html\r\nDate: " + self.GetHttpDateTimeNow() + "\r\n\r\nOK")
2961+
2962+ for retry in range(1,6):
2963+ context=None
2964+ ip = GetIpv4Address()
2965+ if ip == None :
2966+ Log("LoadBalancerProbeServer: GetIpv4Address() returned None, sleeping 10 before retry " + str(retry+1) )
2967+ time.sleep(10)
2968+ else:
2969+ try:
2970+ context = T(ip,port)
2971+ break
2972+ except Exception, e:
2973+ Log("LoadBalancerProbeServer: Exception contructing socket server: " + str(e))
2974+ Log("LoadBalancerProbeServer: Retry socket server construction #" + str(retry+1) )
2975+ return context
2976+
2977+class ConfigurationProvider(object):
2978+ def __init__(self):
2979+ self.values = dict()
2980+ if os.path.isfile("/etc/waagent.conf") == False:
2981+ raise Exception("Missing configuration in /etc/waagent.conf")
2982+ try:
2983+ for line in GetFileContents("/etc/waagent.conf").split('\n'):
2984+ if not line.startswith("#") and "=" in line:
2985+ parts = line.split()[0].split('=')
2986+ value = parts[1].strip("\" ")
2987+ if value != "None":
2988+ self.values[parts[0]] = value
2989+ else:
2990+ self.values[parts[0]] = None
2991+ except:
2992+ Error("Unable to parse /etc/waagent.conf")
2993+ raise
2994+ return
2995+
2996+ def get(self, key):
2997+ return self.values.get(key)
2998+
2999+class EnvMonitor(object):
3000+ def __init__(self):
3001+ self.shutdown = False
3002+ self.HostName = socket.gethostname()
3003+ self.server_thread = threading.Thread(target = self.monitor)
3004+ self.server_thread.setDaemon(True)
3005+ self.server_thread.start()
3006+ self.published = False
3007+
3008+ def monitor(self):
3009+ publish = Config.get("Provisioning.MonitorHostName")
3010+ dhcpcmd = "pidof dhclient"
3011+ if IsSuse():
3012+ dhcpcmd = "pidof dhcpcd"
3013+ if IsDebian():
3014+ dhcpcmd = "pidof dhclient3"
3015+ dhcppid = os.popen(dhcpcmd).read()
3016+ while not self.shutdown:
3017+ for a in RulesFiles:
3018+ if os.path.isfile(a):
3019+ if os.path.isfile(GetLastPathElement(a)):
3020+ os.remove(GetLastPathElement(a))
3021+ shutil.move(a, ".")
3022+ Log("EnvMonitor: Moved " + a + " -> " + LibDir)
3023+ if publish != None and publish.lower().startswith("y"):
3024+ try:
3025+ if socket.gethostname() != self.HostName:
3026+ Log("EnvMonitor: Detected host name change: " + self.HostName + " -> " + socket.gethostname())
3027+ self.HostName = socket.gethostname()
3028+ WaAgent.UpdateAndPublishHostName(self.HostName)
3029+ dhcppid = os.popen(dhcpcmd).read()
3030+ self.published = True
3031+ except:
3032+ pass
3033+ else:
3034+ self.published = True
3035+ pid = ""
3036+ if not os.path.isdir("/proc/" + dhcppid.strip()):
3037+ pid = os.popen(dhcpcmd).read()
3038+ if pid != "" and pid != dhcppid:
3039+ Log("EnvMonitor: Detected dhcp client restart. Restoring routing table.")
3040+ WaAgent.RestoreRoutes()
3041+ dhcppid = pid
3042+ for child in Children:
3043+ if child.poll() != None:
3044+ Children.remove(child)
3045+ time.sleep(5)
3046+
3047+ def SetHostName(self, name):
3048+ if socket.gethostname() == name:
3049+ self.published = True
3050+ elif Run("hostname " + name):
3051+ Error("Error: SetHostName: Cannot set hostname to " + name)
3052+ return ("Error: SetHostName: Cannot set hostname to " + name)
3053+
3054+ def IsNamePublished(self):
3055+ return self.published
3056+
3057+ def ShutdownService(self):
3058+ self.shutdown = True
3059+ self.server_thread.join()
3060+
3061+class Certificates(object):
3062+#
3063+# <CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd">
3064+# <Version>2010-12-15</Version>
3065+# <Incarnation>2</Incarnation>
3066+# <Format>Pkcs7BlobWithPfxContents</Format>
3067+# <Data>MIILTAY...
3068+# </Data>
3069+# </CertificateFile>
3070+#
3071+ def __init__(self):
3072+ self.reinitialize()
3073+
3074+ def reinitialize(self):
3075+ self.Incarnation = None
3076+ self.Role = None
3077+
3078+ def Parse(self, xmlText):
3079+ self.reinitialize()
3080+ SetFileContents("Certificates.xml", xmlText)
3081+ dom = xml.dom.minidom.parseString(xmlText)
3082+ for a in [ "CertificateFile", "Version", "Incarnation",
3083+ "Format", "Data", ]:
3084+ if not dom.getElementsByTagName(a):
3085+ Error("Certificates.Parse: Missing " + a)
3086+ return None
3087+ node = dom.childNodes[0]
3088+ if node.localName != "CertificateFile":
3089+ Error("Certificates.Parse: root not CertificateFile")
3090+ return None
3091+ SetFileContents("Certificates.p7m",
3092+ "MIME-Version: 1.0\n"
3093+ + "Content-Disposition: attachment; filename=\"Certificates.p7m\"\n"
3094+ + "Content-Type: application/x-pkcs7-mime; name=\"Certificates.p7m\"\n"
3095+ + "Content-Transfer-Encoding: base64\n\n"
3096+ + GetNodeTextData(dom.getElementsByTagName("Data")[0]))
3097+ if Run(Openssl + " cms -decrypt -in Certificates.p7m -inkey TransportPrivate.pem -recip TransportCert.pem | " + Openssl + " pkcs12 -nodes -password pass: -out Certificates.pem"):
3098+ Error("Certificates.Parse: Failed to extract certificates from CMS message.")
3099+ return self
3100+ # There may be multiple certificates in this package. Split them.
3101+ file = open("Certificates.pem")
3102+ pindex = 1
3103+ cindex = 1
3104+ output = open("temp.pem", "w")
3105+ for line in file.readlines():
3106+ output.write(line)
3107+ if re.match(r'[-]+END .*?(KEY|CERTIFICATE)[-]+$',line):
3108+ output.close()
3109+ if re.match(r'[-]+END .*?KEY[-]+$',line):
3110+ os.rename("temp.pem", str(pindex) + ".prv")
3111+ pindex += 1
3112+ else:
3113+ os.rename("temp.pem", str(cindex) + ".crt")
3114+ cindex += 1
3115+ output = open("temp.pem", "w")
3116+ output.close()
3117+ os.remove("temp.pem")
3118+ keys = dict()
3119+ index = 1
3120+ filename = str(index) + ".crt"
3121+ while os.path.isfile(filename):
3122+ thumbprint = os.popen(Openssl + " x509 -in " + filename + " -fingerprint -noout").read().rstrip().split('=')[1].replace(':', '').upper()
3123+ pubkey=os.popen(Openssl + " x509 -in " + filename + " -pubkey -noout").read()
3124+ keys[pubkey] = thumbprint
3125+ os.rename(filename, thumbprint + ".crt")
3126+ os.chmod(thumbprint + ".crt", 0600)
3127+ if IsRedHat():
3128+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + thumbprint + ".crt")
3129+ index += 1
3130+ filename = str(index) + ".crt"
3131+ index = 1
3132+ filename = str(index) + ".prv"
3133+ while os.path.isfile(filename):
3134+ pubkey = os.popen(Openssl + " rsa -in " + filename + " -pubout").read()
3135+ os.rename(filename, keys[pubkey] + ".prv")
3136+ os.chmod(keys[pubkey] + ".prv", 0600)
3137+ if IsRedHat():
3138+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + keys[pubkey] + ".prv")
3139+ index += 1
3140+ filename = str(index) + ".prv"
3141+ return self
3142+
3143+class SharedConfig(object):
3144+#
3145+# <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
3146+# <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2">
3147+# <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" />
3148+# <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" />
3149+# </Deployment>
3150+# <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" />
3151+# <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" settleTimeSeconds="10" />
3152+# <LoadBalancerSettings timeoutSeconds="0" waitLoadBalancerProbeCount="8">
3153+# <Probes>
3154+# <Probe name="MachineRole" />
3155+# <Probe name="55B17C5E41A1E1E8FA991CF80FAC8E55" />
3156+# <Probe name="3EA4DBC19418F0A766A4C19D431FA45F" />
3157+# </Probes>
3158+# </LoadBalancerSettings>
3159+# <OutputEndpoints>
3160+# <Endpoint name="MachineRole:Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" type="SFS">
3161+# <Target instance="MachineRole_IN_0" endpoint="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" />
3162+# </Endpoint>
3163+# </OutputEndpoints>
3164+# <Instances>
3165+# <Instance id="MachineRole_IN_0" address="10.115.153.75">
3166+# <FaultDomains randomId="0" updateId="0" updateCount="0" />
3167+# <InputEndpoints>
3168+# <Endpoint name="a" address="10.115.153.75:80" protocol="http" isPublic="true" loadBalancedPublicAddress="70.37.106.197:80" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
3169+# <LocalPorts>
3170+# <LocalPortRange from="80" to="80" />
3171+# </LocalPorts>
3172+# </Endpoint>
3173+# <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" address="10.115.153.75:3389" protocol="tcp" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
3174+# <LocalPorts>
3175+# <LocalPortRange from="3389" to="3389" />
3176+# </LocalPorts>
3177+# </Endpoint>
3178+# <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" address="10.115.153.75:20000" protocol="tcp" isPublic="true" loadBalancedPublicAddress="70.37.106.197:3389" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false">
3179+# <LocalPorts>
3180+# <LocalPortRange from="20000" to="20000" />
3181+# </LocalPorts>
3182+# </Endpoint>
3183+# </InputEndpoints>
3184+# </Instance>
3185+# </Instances>
3186+# </SharedConfig>
3187+#
3188+ def __init__(self):
3189+ self.reinitialize()
3190+
3191+ def reinitialize(self):
3192+ self.Deployment = None
3193+ self.Incarnation = None
3194+ self.Role = None
3195+ self.LoadBalancerSettings = None
3196+ self.OutputEndpoints = None
3197+ self.Instances = None
3198+
3199+ def Parse(self, xmlText):
3200+ self.reinitialize()
3201+ SetFileContents("SharedConfig.xml", xmlText)
3202+ dom = xml.dom.minidom.parseString(xmlText)
3203+ for a in [ "SharedConfig", "Deployment", "Service",
3204+ "ServiceInstance", "Incarnation", "Role", ]:
3205+ if not dom.getElementsByTagName(a):
3206+ Error("SharedConfig.Parse: Missing " + a)
3207+ return None
3208+ node = dom.childNodes[0]
3209+ if node.localName != "SharedConfig":
3210+ Error("SharedConfig.Parse: root not SharedConfig")
3211+ return None
3212+ program = Config.get("Role.TopologyConsumer")
3213+ if program != None:
3214+ Children.append(subprocess.Popen([program, LibDir + "/SharedConfig.xml"]))
3215+ return self
3216+
3217+class HostingEnvironmentConfig(object):
3218+#
3219+# <HostingEnvironmentConfig version="1.0.0.0" goalStateIncarnation="1">
3220+# <StoredCertificates>
3221+# <StoredCertificate name="Stored0Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" certificateId="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" storeName="My" configurationLevel="System" />
3222+# </StoredCertificates>
3223+# <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2">
3224+# <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" />
3225+# <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" />
3226+# </Deployment>
3227+# <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" />
3228+# <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" hostingEnvironmentVersion="1" software="" softwareType="ApplicationPackage" entryPoint="" parameters="" settleTimeSeconds="10" />
3229+# <HostingEnvironmentSettings name="full" Runtime="rd_fabric_stable.110217-1402.RuntimePackage_1.0.0.8.zip">
3230+# <CAS mode="full" />
3231+# <PrivilegeLevel mode="max" />
3232+# <AdditionalProperties><CgiHandlers></CgiHandlers></AdditionalProperties>
3233+# </HostingEnvironmentSettings>
3234+# <ApplicationSettings>
3235+# <Setting name="__ModelData" value="&lt;m role=&quot;MachineRole&quot; xmlns=&quot;urn:azure:m:v1&quot;>&lt;r name=&quot;MachineRole&quot;>&lt;e name=&quot;a&quot; />&lt;e name=&quot;b&quot; />&lt;e name=&quot;Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp&quot; />&lt;e name=&quot;Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput&quot; />&lt;/r>&lt;/m>" />
3236+# <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=http;AccountName=osimages;AccountKey=DNZQ..." />
3237+# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword" value="MIIBnQYJKoZIhvcN..." />
3238+# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration" value="2022-07-23T23:59:59.0000000-07:00" />
3239+# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername" value="test" />
3240+# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled" value="true" />
3241+# <Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" />
3242+# <Setting name="Certificate|Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" value="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" />
3243+# </ApplicationSettings>
3244+# <ResourceReferences>
3245+# <Resource name="DiagnosticStore" type="directory" request="Microsoft.Cis.Fabric.Controller.Descriptions.ServiceDescription.Data.Policy" sticky="true" size="1" path="db00a7755a5e4e8a8fe4b19bc3b330c3.MachineRole.DiagnosticStore\" disableQuota="false" />
3246+# </ResourceReferences>
3247+# </HostingEnvironmentConfig>
3248+#
3249+ def __init__(self):
3250+ self.reinitialize()
3251+
3252+ def reinitialize(self):
3253+ self.StoredCertificates = None
3254+ self.Deployment = None
3255+ self.Incarnation = None
3256+ self.Role = None
3257+ self.HostingEnvironmentSettings = None
3258+ self.ApplicationSettings = None
3259+ self.Certificates = None
3260+ self.ResourceReferences = None
3261+
3262+ def Parse(self, xmlText):
3263+ self.reinitialize()
3264+ SetFileContents("HostingEnvironmentConfig.xml", xmlText)
3265+ dom = xml.dom.minidom.parseString(xmlText)
3266+ for a in [ "HostingEnvironmentConfig", "Deployment", "Service",
3267+ "ServiceInstance", "Incarnation", "Role", ]:
3268+ if not dom.getElementsByTagName(a):
3269+ Error("HostingEnvironmentConfig.Parse: Missing " + a)
3270+ return None
3271+ node = dom.childNodes[0]
3272+ if node.localName != "HostingEnvironmentConfig":
3273+ Error("HostingEnvironmentConfig.Parse: root not HostingEnvironmentConfig")
3274+ return None
3275+ self.ApplicationSettings = dom.getElementsByTagName("Setting")
3276+ self.Certificates = dom.getElementsByTagName("StoredCertificate")
3277+ return self
3278+
3279+ def DecryptPassword(self, e):
3280+ SetFileContents("password.p7m",
3281+ "MIME-Version: 1.0\n"
3282+ + "Content-Disposition: attachment; filename=\"password.p7m\"\n"
3283+ + "Content-Type: application/x-pkcs7-mime; name=\"password.p7m\"\n"
3284+ + "Content-Transfer-Encoding: base64\n\n"
3285+ + textwrap.fill(e, 64))
3286+ return os.popen(Openssl + " cms -decrypt -in password.p7m -inkey Certificates.pem -recip Certificates.pem").read()
3287+
3288+ def ActivateResourceDisk(self):
3289+ global DiskActivated
3290+ if IsWindows():
3291+ DiskActivated = True
3292+ Log("Skipping ActivateResourceDisk on Windows")
3293+ return
3294+ format = Config.get("ResourceDisk.Format")
3295+ if format == None or format.lower().startswith("n"):
3296+ DiskActivated = True
3297+ return
3298+ device = DeviceForIdePort(1)
3299+ if device == None:
3300+ Error("ActivateResourceDisk: Unable to detect disk topology.")
3301+ return
3302+ device = "/dev/" + device
3303+ for entry in os.popen("mount").read().split():
3304+ if entry.startswith(device + "1"):
3305+ Log("ActivateResourceDisk: " + device + "1 is already mounted.")
3306+ DiskActivated = True
3307+ return
3308+ mountpoint = Config.get("ResourceDisk.MountPoint")
3309+ if mountpoint == None:
3310+ mountpoint = "/mnt/resource"
3311+ CreateDir(mountpoint, "root", 0755)
3312+ fs = Config.get("ResourceDisk.Filesystem")
3313+ if fs == None:
3314+ fs = "ext3"
3315+ if os.popen("sfdisk -q -c " + device + " 1").read().rstrip() == "7" and fs != "ntfs":
3316+ Run("sfdisk -c " + device + " 1 83")
3317+ Run("mkfs." + fs + " " + device + "1")
3318+ if Run("mount " + device + "1 " + mountpoint):
3319+ Error("ActivateResourceDisk: Failed to mount resource disk (" + device + "1).")
3320+ return
3321+ Log("Resource disk (" + device + "1) is mounted at " + mountpoint + " with fstype " + fs)
3322+ DiskActivated = True
3323+ swap = Config.get("ResourceDisk.EnableSwap")
3324+ if swap == None or swap.lower().startswith("n"):
3325+ return
3326+ sizeKB = int(Config.get("ResourceDisk.SwapSizeMB")) * 1024
3327+ if os.path.isfile(mountpoint + "/swapfile") and os.path.getsize(mountpoint + "/swapfile") != (sizeKB * 1024):
3328+ os.remove(mountpoint + "/swapfile")
3329+ if not os.path.isfile(mountpoint + "/swapfile"):
3330+ Run("dd if=/dev/zero of=" + mountpoint + "/swapfile bs=1024 count=" + str(sizeKB))
3331+ Run("mkswap " + mountpoint + "/swapfile")
3332+ if not Run("swapon " + mountpoint + "/swapfile"):
3333+ Log("Enabled " + str(sizeKB) + " KB of swap at " + mountpoint + "/swapfile")
3334+ else:
3335+ Error("ActivateResourceDisk: Failed to activate swap at " + mountpoint + "/swapfile")
3336+
3337+ def Process(self):
3338+ if DiskActivated == False:
3339+ diskThread = threading.Thread(target = self.ActivateResourceDisk)
3340+ diskThread.start()
3341+ User = None
3342+ Pass = None
3343+ Expiration = None
3344+ Thumbprint = None
3345+ for b in self.ApplicationSettings:
3346+ sname = b.getAttribute("name")
3347+ svalue = b.getAttribute("value")
3348+ if sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountEncryptedPassword":
3349+ Pass = self.DecryptPassword(svalue)
3350+ elif sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountUsername":
3351+ User = svalue
3352+ elif sname == "Microsoft.WindowsAzure.Plugins.RemoteAccess.AccountExpiration":
3353+ Expiration = svalue
3354+ elif sname == "Certificate|Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption":
3355+ Thumbprint = svalue.split(':')[1].upper()
3356+ if User != None and Pass != None:
3357+ if User != "root" and User != "" and Pass != "":
3358+ CreateAccount(User, Pass, Expiration, Thumbprint)
3359+ else:
3360+ Error("Not creating user account: " + User)
3361+ for c in self.Certificates:
3362+ cname = c.getAttribute("name")
3363+ csha1 = c.getAttribute("certificateId").split(':')[1].upper()
3364+ cpath = c.getAttribute("storeName")
3365+ clevel = c.getAttribute("configurationLevel")
3366+ if os.path.isfile(csha1 + ".prv"):
3367+ Log("Private key with thumbprint: " + csha1 + " was retrieved.")
3368+ if os.path.isfile(csha1 + ".crt"):
3369+ Log("Public cert with thumbprint: " + csha1 + " was retrieved.")
3370+ program = Config.get("Role.ConfigurationConsumer")
3371+ if program != None:
3372+ Children.append(subprocess.Popen([program, LibDir + "/HostingEnvironmentConfig.xml"]))
3373+
3374+class GoalState(Util):
3375+#
3376+# <GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
3377+# <Version>2010-12-15</Version>
3378+# <Incarnation>1</Incarnation>
3379+# <Machine>
3380+# <ExpectedState>Started</ExpectedState>
3381+# <LBProbePorts>
3382+# <Port>16001</Port>
3383+# </LBProbePorts>
3384+# </Machine>
3385+# <Container>
3386+# <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId>
3387+# <RoleInstanceList>
3388+# <RoleInstance>
3389+# <InstanceId>MachineRole_IN_0</InstanceId>
3390+# <State>Started</State>
3391+# <Configuration>
3392+# <HostingEnvironmentConfig>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=config&amp;type=hostingEnvironmentConfig&amp;incarnation=1</HostingEnvironmentConfig>
3393+# <SharedConfig>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=config&amp;type=sharedConfig&amp;incarnation=1</SharedConfig>
3394+# <Certificates>http://10.115.153.40:80/machine/c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2/MachineRole%5FIN%5F0?comp=certificates&amp;incarnation=1</Certificates>
3395+# </Configuration>
3396+# </RoleInstance>
3397+# </RoleInstanceList>
3398+# </Container>
3399+# </GoalState>
3400+#
3401+# There is only one Role for VM images.
3402+#
3403+# Of primary interest is:
3404+# Machine/ExpectedState -- this is how shutdown is requested
3405+# LBProbePorts -- an http server needs to run here
3406+# We also note Container/ContainerID and RoleInstance/InstanceId to form the health report.
3407+# And of course, Incarnation
3408+#
3409+ def __init__(self, Agent):
3410+ self.Agent = Agent
3411+ self.Endpoint = Agent.Endpoint
3412+ self.TransportCert = Agent.TransportCert
3413+ self.reinitialize()
3414+
3415+ def reinitialize(self):
3416+ self.Incarnation = None # integer
3417+ self.ExpectedState = None # "Started" or "Stopped"
3418+ self.HostingEnvironmentConfigUrl = None
3419+ self.HostingEnvironmentConfigXml = None
3420+ self.HostingEnvironmentConfig = None
3421+ self.SharedConfigUrl = None
3422+ self.SharedConfigXml = None
3423+ self.SharedConfig = None
3424+ self.CertificatesUrl = None
3425+ self.CertificatesXml = None
3426+ self.Certificates = None
3427+ self.RoleInstanceId = None
3428+ self.ContainerId = None
3429+ self.LoadBalancerProbePort = None # integer, ?list of integers
3430+
3431+ def Parse(self, xmlText):
3432+ self.reinitialize()
3433+ node = xml.dom.minidom.parseString(xmlText).childNodes[0]
3434+ if node.localName != "GoalState":
3435+ Error("GoalState.Parse: root not GoalState")
3436+ return None
3437+ for a in node.childNodes:
3438+ if a.nodeType == node.ELEMENT_NODE:
3439+ if a.localName == "Incarnation":
3440+ self.Incarnation = GetNodeTextData(a)
3441+ elif a.localName == "Machine":
3442+ for b in a.childNodes:
3443+ if b.nodeType == node.ELEMENT_NODE:
3444+ if b.localName == "ExpectedState":
3445+ self.ExpectedState = GetNodeTextData(b)
3446+ Log("ExpectedState: " + self.ExpectedState)
3447+ elif b.localName == "LBProbePorts":
3448+ for c in b.childNodes:
3449+ if c.nodeType == node.ELEMENT_NODE and c.localName == "Port":
3450+ self.LoadBalancerProbePort = int(GetNodeTextData(c))
3451+ elif a.localName == "Container":
3452+ for b in a.childNodes:
3453+ if b.nodeType == node.ELEMENT_NODE:
3454+ if b.localName == "ContainerId":
3455+ self.ContainerId = GetNodeTextData(b)
3456+ Log("ContainerId: " + self.ContainerId)
3457+ elif b.localName == "RoleInstanceList":
3458+ for c in b.childNodes:
3459+ if c.localName == "RoleInstance":
3460+ for d in c.childNodes:
3461+ if d.nodeType == node.ELEMENT_NODE:
3462+ if d.localName == "InstanceId":
3463+ self.RoleInstanceId = GetNodeTextData(d)
3464+ Log("RoleInstanceId: " + self.RoleInstanceId)
3465+ elif d.localName == "State":
3466+ pass
3467+ elif d.localName == "Configuration":
3468+ for e in d.childNodes:
3469+ if e.nodeType == node.ELEMENT_NODE:
3470+ if e.localName == "HostingEnvironmentConfig":
3471+ self.HostingEnvironmentConfigUrl = GetNodeTextData(e)
3472+ LogIfVerbose("HostingEnvironmentConfigUrl:" + self.HostingEnvironmentConfigUrl)
3473+ self.HostingEnvironmentConfigXml = self.HttpGetWithHeaders(self.HostingEnvironmentConfigUrl)
3474+ self.HostingEnvironmentConfig = HostingEnvironmentConfig().Parse(self.HostingEnvironmentConfigXml)
3475+ elif e.localName == "SharedConfig":
3476+ self.SharedConfigUrl = GetNodeTextData(e)
3477+ LogIfVerbose("SharedConfigUrl:" + self.SharedConfigUrl)
3478+ self.SharedConfigXml = self.HttpGetWithHeaders(self.SharedConfigUrl)
3479+ self.SharedConfig = SharedConfig().Parse(self.SharedConfigXml)
3480+ elif e.localName == "Certificates":
3481+ self.CertificatesUrl = GetNodeTextData(e)
3482+ LogIfVerbose("CertificatesUrl:" + self.CertificatesUrl)
3483+ self.CertificatesXml = self.HttpSecureGetWithHeaders(self.CertificatesUrl, self.TransportCert)
3484+ self.Certificates = Certificates().Parse(self.CertificatesXml)
3485+ if self.Incarnation == None:
3486+ Error("GoalState.Parse: Incarnation missing")
3487+ return None
3488+ if self.ExpectedState == None:
3489+ Error("GoalState.Parse: ExpectedState missing")
3490+ return None
3491+ if self.RoleInstanceId == None:
3492+ Error("GoalState.Parse: RoleInstanceId missing")
3493+ return None
3494+ if self.ContainerId == None:
3495+ Error("GoalState.Parse: ContainerId missing")
3496+ return None
3497+ SetFileContents("GoalState." + self.Incarnation + ".xml", xmlText)
3498+ return self
3499+
3500+ def Process(self):
3501+ self.HostingEnvironmentConfig.Process()
3502+
3503+class OvfEnv(object):
3504+#
3505+# <?xml version="1.0" encoding="utf-8"?>
3506+# <Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3507+# <wa:ProvisioningSection>
3508+# <wa:Version>1.0</wa:Version>
3509+# <LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
3510+# <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
3511+# <HostName>HostName</HostName>
3512+# <UserName>UserName</UserName>
3513+# <UserPassword>UserPassword</UserPassword>
3514+# <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication>
3515+# <SSH>
3516+# <PublicKeys>
3517+# <PublicKey>
3518+# <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
3519+# <Path>$HOME/UserName/.ssh/authorized_keys</Path>
3520+# </PublicKey>
3521+# </PublicKeys>
3522+# <KeyPairs>
3523+# <KeyPair>
3524+# <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
3525+# <Path>$HOME/UserName/.ssh/id_rsa</Path>
3526+# </KeyPair>
3527+# </KeyPairs>
3528+# </SSH>
3529+# </LinuxProvisioningConfigurationSet>
3530+# </wa:ProvisioningSection>
3531+# </Environment>
3532+#
3533+ def __init__(self):
3534+ self.reinitialize()
3535+
3536+ def reinitialize(self):
3537+ self.WaNs = "http://schemas.microsoft.com/windowsazure"
3538+ self.OvfNs = "http://schemas.dmtf.org/ovf/environment/1"
3539+ self.MajorVersion = 1
3540+ self.MinorVersion = 0
3541+ self.ComputerName = None
3542+ self.AdminPassword = None
3543+ self.UserName = None
3544+ self.UserPassword = None
3545+ self.DisableSshPasswordAuthentication = True
3546+ self.SshPublicKeys = []
3547+ self.SshKeyPairs = []
3548+
3549+ def Parse(self, xmlText):
3550+ self.reinitialize()
3551+ dom = xml.dom.minidom.parseString(xmlText)
3552+ if len(dom.getElementsByTagNameNS(self.OvfNs, "Environment")) != 1:
3553+ Error("Unable to parse OVF XML.")
3554+ section = None
3555+ newer = False
3556+ for p in dom.getElementsByTagNameNS(self.WaNs, "ProvisioningSection"):
3557+ for n in p.childNodes:
3558+ if n.localName == "Version":
3559+ verparts = GetNodeTextData(n).split('.')
3560+ major = int(verparts[0])
3561+ minor = int(verparts[1])
3562+ if major > self.MajorVersion:
3563+ newer = True
3564+ if major != self.MajorVersion:
3565+ break
3566+ if minor > self.MinorVersion:
3567+ newer = True
3568+ section = p
3569+ if newer == True:
3570+ Warn("Newer provisioning configuration detected. Please consider updating waagent.")
3571+ if section == None:
3572+ Error("Could not find ProvisioningSection with major version=" + str(self.MajorVersion))
3573+ return None
3574+ self.ComputerName = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "HostName")[0])
3575+ self.UserName = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "UserName")[0])
3576+ try:
3577+ self.UserPassword = GetNodeTextData(section.getElementsByTagNameNS(self.WaNs, "UserPassword")[0])
3578+ except:
3579+ pass
3580+ disableSshPass = section.getElementsByTagNameNS(self.WaNs, "DisableSshPasswordAuthentication")
3581+ if len(disableSshPass) != 0:
3582+ self.DisableSshPasswordAuthentication = (GetNodeTextData(disableSshPass[0]).lower() == "true")
3583+ for pkey in section.getElementsByTagNameNS(self.WaNs, "PublicKey"):
3584+ fp = None
3585+ path = None
3586+ for c in pkey.childNodes:
3587+ if c.localName == "Fingerprint":
3588+ fp = GetNodeTextData(c).upper()
3589+ if c.localName == "Path":
3590+ path = GetNodeTextData(c)
3591+ self.SshPublicKeys += [[fp, path]]
3592+ for keyp in section.getElementsByTagNameNS(self.WaNs, "KeyPair"):
3593+ fp = None
3594+ path = None
3595+ for c in keyp.childNodes:
3596+ if c.localName == "Fingerprint":
3597+ fp = GetNodeTextData(c).upper()
3598+ if c.localName == "Path":
3599+ path = GetNodeTextData(c)
3600+ self.SshKeyPairs += [[fp, path]]
3601+ return self
3602+
3603+ def PrepareDir(self, filepath):
3604+ home = GetHome()
3605+ # Expand HOME variable if present in path
3606+ path = os.path.normpath(filepath.replace("$HOME", home))
3607+ if (path.startswith("/") == False) or (path.endswith("/") == True):
3608+ return None
3609+ dir = path.rsplit('/', 1)[0]
3610+ if dir != "":
3611+ CreateDir(dir, "root", 0700)
3612+ if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
3613+ ChangeOwner(dir, self.UserName)
3614+ return path
3615+
3616+ def NumberToBytes(self, i):
3617+ result = []
3618+ while i:
3619+ result.append(chr(i & 0xFF))
3620+ i >>= 8
3621+ result.reverse()
3622+ return ''.join(result)
3623+
3624+ def BitsToString(self, a):
3625+ index=7
3626+ s = ""
3627+ c = 0
3628+ for bit in a:
3629+ c = c | (bit << index)
3630+ index = index - 1
3631+ if index == -1:
3632+ s = s + struct.pack('>B', c)
3633+ c = 0
3634+ index = 7
3635+ return s
3636+
3637+ def OpensslToSsh(self, file):
3638+ from pyasn1.codec.der import decoder as der_decoder
3639+ try:
3640+ f = open(file).read().replace('\n','').split("KEY-----")[1].split('-')[0]
3641+ k=der_decoder.decode(self.BitsToString(der_decoder.decode(base64.b64decode(f))[0][1]))[0]
3642+ n=k[0]
3643+ e=k[1]
3644+ keydata=""
3645+ keydata += struct.pack('>I',len("ssh-rsa"))
3646+ keydata += "ssh-rsa"
3647+ keydata += struct.pack('>I',len(self.NumberToBytes(e)))
3648+ keydata += self.NumberToBytes(e)
3649+ keydata += struct.pack('>I',len(self.NumberToBytes(n)) + 1)
3650+ keydata += "\0"
3651+ keydata += self.NumberToBytes(n)
3652+ except Exception, e:
3653+ print("OpensslToSsh: Exception " + str(e))
3654+ return None
3655+ return "ssh-rsa " + base64.b64encode(keydata) + "\n"
3656+
3657+ def Process(self):
3658+ error = None
3659+ error=WaAgent.EnvMonitor.SetHostName(self.ComputerName)
3660+ if error: return error
3661+ if self.DisableSshPasswordAuthentication:
3662+ filepath = "/etc/ssh/sshd_config"
3663+ # Disable RFC 4252 and RFC 4256 authentication schemes.
3664+ ReplaceFileContentsAtomic(filepath, "\n".join(filter(lambda a: not
3665+ (a.startswith("PasswordAuthentication") or a.startswith("ChallengeResponseAuthentication")),
3666+ GetFileContents(filepath).split('\n'))) + "PasswordAuthentication no\nChallengeResponseAuthentication no\n")
3667+ Log("Disabled SSH password-based authentication methods.")
3668+ if self.AdminPassword != None:
3669+ os.popen("chpasswd", "w").write("root:" + self.AdminPassword + "\n")
3670+ if self.UserName != None:
3671+ error = CreateAccount(self.UserName, self.UserPassword, None, None)
3672+ sel = os.popen("getenforce").read().startswith("Enforcing")
3673+ if sel == True and IsRedHat():
3674+ Run("setenforce 0")
3675+ home = GetHome()
3676+ for pkey in self.SshPublicKeys:
3677+ if not os.path.isfile(pkey[0] + ".crt"):
3678+ Error("PublicKey not found: " + pkey[0])
3679+ error = "Failed to deploy public key (0x09)."
3680+ continue
3681+ path = self.PrepareDir(pkey[1])
3682+ if path == None:
3683+ Error("Invalid path: " + pkey[1] + " for PublicKey: " + pkey[0])
3684+ error = "Invalid path for public key (0x03)."
3685+ continue
3686+ Run(Openssl + " x509 -in " + pkey[0] + ".crt -noout -pubkey > " + pkey[0] + ".pub")
3687+ if IsRedHat():
3688+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + pkey[0] + ".pub")
3689+ if IsUbuntu():
3690+ # Only supported in new SSH releases
3691+ Run("ssh-keygen -i -m PKCS8 -f " + pkey[0] + ".pub >> " + path)
3692+ else:
3693+ SshPubKey = self.OpensslToSsh(pkey[0] + ".pub")
3694+ if SshPubKey != None:
3695+ AppendFileContents(path, SshPubKey)
3696+ else:
3697+ Error("Failed: " + pkey[0] + ".crt -> " + path)
3698+ error = "Failed to deploy public key (0x04)."
3699+ if IsRedHat():
3700+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path)
3701+ if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
3702+ ChangeOwner(path, self.UserName)
3703+ for keyp in self.SshKeyPairs:
3704+ if not os.path.isfile(keyp[0] + ".prv"):
3705+ Error("KeyPair not found: " + keyp[0])
3706+ error = "Failed to deploy key pair (0x0A)."
3707+ continue
3708+ path = self.PrepareDir(keyp[1])
3709+ if path == None:
3710+ Error("Invalid path: " + keyp[1] + " for KeyPair: " + keyp[0])
3711+ error = "Invalid path for key pair (0x05)."
3712+ continue
3713+ SetFileContents(path, GetFileContents(keyp[0] + ".prv"))
3714+ os.chmod(path, 0600)
3715+ Run("ssh-keygen -y -f " + keyp[0] + ".prv > " + path + ".pub")
3716+ if IsRedHat():
3717+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path)
3718+ Run("chcon unconfined_u:object_r:ssh_home_t:s0 " + path + ".pub")
3719+ if path.startswith(os.path.normpath(home + "/" + self.UserName + "/")):
3720+ ChangeOwner(path, self.UserName)
3721+ ChangeOwner(path + ".pub", self.UserName)
3722+ if sel == True and IsRedHat():
3723+ Run("setenforce 1")
3724+ while not WaAgent.EnvMonitor.IsNamePublished():
3725+ time.sleep(1)
3726+ ReloadSshd()
3727+ return error
3728+
3729+def UpdateAndPublishHostNameCommon(name):
3730+ # RedHat
3731+ if IsRedHat():
3732+ filepath = "/etc/sysconfig/network"
3733+ if os.path.isfile(filepath):
3734+ ReplaceFileContentsAtomic(filepath, "HOSTNAME=" + name + "\n"
3735+ + "\n".join(filter(lambda a: not a.startswith("HOSTNAME"), GetFileContents(filepath).split('\n'))))
3736+
3737+ for ethernetInterface in PossibleEthernetInterfaces:
3738+ filepath = "/etc/sysconfig/network-scripts/ifcfg-" + ethernetInterface
3739+ if os.path.isfile(filepath):
3740+ ReplaceFileContentsAtomic(filepath, "DHCP_HOSTNAME=" + name + "\n"
3741+ + "\n".join(filter(lambda a: not a.startswith("DHCP_HOSTNAME"), GetFileContents(filepath).split('\n'))))
3742+
3743+ # Debian
3744+ if IsDebian():
3745+ SetFileContents("/etc/hostname", name)
3746+
3747+ for filepath in EtcDhcpClientConfFiles:
3748+ if os.path.isfile(filepath):
3749+ ReplaceFileContentsAtomic(filepath, "send host-name \"" + name + "\";\n"
3750+ + "\n".join(filter(lambda a: not a.startswith("send host-name"), GetFileContents(filepath).split('\n'))))
3751+
3752+ # Suse
3753+ if IsSuse():
3754+ SetFileContents("/etc/HOSTNAME", name)
3755+
3756+class Agent(Util):
3757+ def __init__(self):
3758+ self.GoalState = None
3759+ self.Endpoint = None
3760+ self.LoadBalancerProbeServer = None
3761+ self.HealthReportCounter = 0
3762+ self.TransportCert = ""
3763+ self.EnvMonitor = None
3764+ self.SendData = None
3765+ self.DhcpResponse = None
3766+
3767+ def CheckVersions(self):
3768+ #<?xml version="1.0" encoding="utf-8"?>
3769+ #<Versions>
3770+ # <Preferred>
3771+ # <Version>2010-12-15</Version>
3772+ # </Preferred>
3773+ # <Supported>
3774+ # <Version>2010-12-15</Version>
3775+ # <Version>2010-28-10</Version>
3776+ # </Supported>
3777+ #</Versions>
3778+ global ProtocolVersion
3779+ protocolVersionSeen = False
3780+ node = xml.dom.minidom.parseString(self.HttpGetWithoutHeaders("/?comp=versions")).childNodes[0]
3781+ if node.localName != "Versions":
3782+ Error("CheckVersions: root not Versions")
3783+ return False
3784+ for a in node.childNodes:
3785+ if a.nodeType == node.ELEMENT_NODE and a.localName == "Supported":
3786+ for b in a.childNodes:
3787+ if b.nodeType == node.ELEMENT_NODE and b.localName == "Version":
3788+ v = GetNodeTextData(b)
3789+ LogIfVerbose("Fabric supported wire protocol version: " + v)
3790+ if v == ProtocolVersion:
3791+ protocolVersionSeen = True
3792+ if a.nodeType == node.ELEMENT_NODE and a.localName == "Preferred":
3793+ v = GetNodeTextData(a.getElementsByTagName("Version")[0])
3794+ LogIfVerbose("Fabric preferred wire protocol version: " + v)
3795+ if ProtocolVersion < v:
3796+ Warn("Newer wire protocol version detected. Please consider updating waagent.")
3797+ if not protocolVersionSeen:
3798+ Warn("Agent supported wire protocol version: " + ProtocolVersion + " was not advertised by Fabric.")
3799+ ProtocolVersion = "2011-08-31"
3800+ Log("Negotiated wire protocol version: " + ProtocolVersion)
3801+ return True
3802+
3803+ def Unpack(self, buffer, offset, range):
3804+ result = 0
3805+ for i in range:
3806+ result = (result << 8) | Ord(buffer[offset + i])
3807+ return result
3808+
3809+ def UnpackLittleEndian(self, buffer, offset, length):
3810+ return self.Unpack(buffer, offset, range(length - 1, -1, -1))
3811+
3812+ def UnpackBigEndian(self, buffer, offset, length):
3813+ return self.Unpack(buffer, offset, range(0, length))
3814+
3815+ def HexDump3(self, buffer, offset, length):
3816+ return ''.join(['%02X' % Ord(char) for char in buffer[offset:offset + length]])
3817+
3818+ def HexDump2(self, buffer):
3819+ return self.HexDump3(buffer, 0, len(buffer))
3820+
3821+ def BuildDhcpRequest(self):
3822+ #
3823+ # typedef struct _DHCP {
3824+ # UINT8 Opcode; /* op: BOOTREQUEST or BOOTREPLY */
3825+ # UINT8 HardwareAddressType; /* htype: ethernet */
3826+ # UINT8 HardwareAddressLength; /* hlen: 6 (48 bit mac address) */
3827+ # UINT8 Hops; /* hops: 0 */
3828+ # UINT8 TransactionID[4]; /* xid: random */
3829+ # UINT8 Seconds[2]; /* secs: 0 */
3830+ # UINT8 Flags[2]; /* flags: 0 or 0x8000 for broadcast */
3831+ # UINT8 ClientIpAddress[4]; /* ciaddr: 0 */
3832+ # UINT8 YourIpAddress[4]; /* yiaddr: 0 */
3833+ # UINT8 ServerIpAddress[4]; /* siaddr: 0 */
3834+ # UINT8 RelayAgentIpAddress[4]; /* giaddr: 0 */
3835+ # UINT8 ClientHardwareAddress[16]; /* chaddr: 6 byte ethernet MAC address */
3836+ # UINT8 ServerName[64]; /* sname: 0 */
3837+ # UINT8 BootFileName[128]; /* file: 0 */
3838+ # UINT8 MagicCookie[4]; /* 99 130 83 99 */
3839+ # /* 0x63 0x82 0x53 0x63 */
3840+ # /* options -- hard code ours */
3841+ #
3842+ # UINT8 MessageTypeCode; /* 53 */
3843+ # UINT8 MessageTypeLength; /* 1 */
3844+ # UINT8 MessageType; /* 1 for DISCOVER */
3845+ # UINT8 End; /* 255 */
3846+ # } DHCP;
3847+ #
3848+
3849+ # tuple of 244 zeros
3850+ # (struct.pack_into would be good here, but requires Python 2.5)
3851+ sendData = [0] * 244
3852+
3853+ transactionID = os.urandom(4)
3854+ macAddress = GetMacAddress()
3855+
3856+ # Opcode = 1
3857+ # HardwareAddressType = 1 (ethernet/MAC)
3858+ # HardwareAddressLength = 6 (ethernet/MAC/48 bits)
3859+ for a in range(0, 3):
3860+ sendData[a] = [1, 1, 6][a]
3861+
3862+ # fill in transaction id (random number to ensure response matches request)
3863+ for a in range(0, 4):
3864+ sendData[4 + a] = Ord(transactionID[a])
3865+
3866+ LogIfVerbose("BuildDhcpRequest: transactionId:%s,%04X" % (self.HexDump2(transactionID), self.UnpackBigEndian(sendData, 4, 4)))
3867+
3868+ # fill in ClientHardwareAddress
3869+ for a in range(0, 6):
3870+ sendData[0x1C + a] = Ord(macAddress[a])
3871+
3872+ # DHCP Magic Cookie: 99, 130, 83, 99
3873+ # MessageTypeCode = 53 DHCP Message Type
3874+ # MessageTypeLength = 1
3875+ # MessageType = DHCPDISCOVER
3876+ # End = 255 DHCP_END
3877+ for a in range(0, 8):
3878+ sendData[0xEC + a] = [99, 130, 83, 99, 53, 1, 1, 255][a]
3879+ return array.array("c", map(chr, sendData))
3880+
3881+ def IntegerToIpAddressV4String(self, a):
3882+ return "%u.%u.%u.%u" % ((a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF)
3883+
3884+ def RouteAdd(self, net, mask, gateway):
3885+ if IsWindows():
3886+ return
3887+ net = self.IntegerToIpAddressV4String(net)
3888+ mask = self.IntegerToIpAddressV4String(mask)
3889+ gateway = self.IntegerToIpAddressV4String(gateway)
3890+ Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway)
3891+
3892+ def HandleDhcpResponse(self, sendData, receiveBuffer):
3893+ LogIfVerbose("HandleDhcpResponse")
3894+ bytesReceived = len(receiveBuffer)
3895+ if bytesReceived < 0xF6:
3896+ Error("HandleDhcpResponse: Too few bytes received " + str(bytesReceived))
3897+ return None
3898+
3899+ LogIfVerbose("BytesReceived: " + hex(bytesReceived))
3900+ LogWithPrefixIfVerbose("DHCP response:", HexDump(receiveBuffer, bytesReceived))
3901+
3902+ # check transactionId, cookie, MAC address
3903+ # cookie should never mismatch
3904+ # transactionId and MAC address may mismatch if we see a response meant from another machine
3905+
3906+ for offsets in [range(4, 4 + 4), range(0x1C, 0x1C + 6), range(0xEC, 0xEC + 4)]:
3907+ for offset in offsets:
3908+ sentByte = Ord(sendData[offset])
3909+ receivedByte = Ord(receiveBuffer[offset])
3910+ if sentByte != receivedByte:
3911+ LogIfVerbose("HandleDhcpResponse: sent cookie:" + self.HexDump3(sendData, 0xEC, 4))
3912+ LogIfVerbose("HandleDhcpResponse: rcvd cookie:" + self.HexDump3(receiveBuffer, 0xEC, 4))
3913+ LogIfVerbose("HandleDhcpResponse: sent transactionID:" + self.HexDump3(sendData, 4, 4))
3914+ LogIfVerbose("HandleDhcpResponse: rcvd transactionID:" + self.HexDump3(receiveBuffer, 4, 4))
3915+ LogIfVerbose("HandleDhcpResponse: sent ClientHardwareAddress:" + self.HexDump3(sendData, 0x1C, 6))
3916+ LogIfVerbose("HandleDhcpResponse: rcvd ClientHardwareAddress:" + self.HexDump3(receiveBuffer, 0x1C, 6))
3917+ LogIfVerbose("HandleDhcpResponse: transactionId, cookie, or MAC address mismatch")
3918+ return None
3919+ endpoint = None
3920+
3921+ #
3922+ # Walk all the returned options, parsing out what we need, ignoring the others.
3923+ # We need the custom option 245 to find the the endpoint we talk to,
3924+ # as well as, to handle some Linux DHCP client incompatibilities,
3925+ # options 3 for default gateway and 249 for routes. And 255 is end.
3926+ #
3927+
3928+ i = 0xF0 # offset to first option
3929+ while i < bytesReceived:
3930+ option = Ord(receiveBuffer[i])
3931+ length = 0
3932+ if (i + 1) < bytesReceived:
3933+ length = Ord(receiveBuffer[i + 1])
3934+ LogIfVerbose("DHCP option " + hex(option) + " at offset:" + hex(i) + " with length:" + hex(length))
3935+ if option == 255:
3936+ LogIfVerbose("DHCP packet ended at offset " + hex(i))
3937+ break
3938+ elif option == 249:
3939+ # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
3940+ LogIfVerbose("Routes at offset:" + hex(i) + " with length:" + hex(length))
3941+ if length < 5:
3942+ Error("Data too small for option " + option)
3943+ j = i + 2
3944+ while j < (i + length + 2):
3945+ maskLengthBits = Ord(receiveBuffer[j])
3946+ maskLengthBytes = (((maskLengthBits + 7) & ~7) >> 3)
3947+ mask = 0xFFFFFFFF & (0xFFFFFFFF << (32 - maskLengthBits))
3948+ j += 1
3949+ net = self.UnpackBigEndian(receiveBuffer, j, maskLengthBytes)
3950+ net <<= (32 - maskLengthBytes * 8)
3951+ net &= mask
3952+ j += maskLengthBytes
3953+ gateway = self.UnpackBigEndian(receiveBuffer, j, 4)
3954+ j += 4
3955+ self.RouteAdd(net, mask, gateway)
3956+ if j != (i + length + 2):
3957+ Error("HandleDhcpResponse: Unable to parse routes")
3958+ elif option == 3 or option == 245:
3959+ if i + 5 < bytesReceived:
3960+ if length != 4:
3961+ Error("HandleDhcpResponse: Endpoint or Default Gateway not 4 bytes")
3962+ return None
3963+ gateway = self.UnpackBigEndian(receiveBuffer, i + 2, 4)
3964+ IpAddress = self.IntegerToIpAddressV4String(gateway)
3965+ if option == 3:
3966+ self.RouteAdd(0, 0, gateway)
3967+ name = "DefaultGateway"
3968+ else:
3969+ endpoint = IpAddress
3970+ name = "Windows Azure wire protocol endpoint"
3971+ LogIfVerbose(name + ": " + IpAddress + " at " + hex(i))
3972+ else:
3973+ Error("HandleDhcpResponse: Data too small for option " + option)
3974+ else:
3975+ LogIfVerbose("Skipping DHCP option " + hex(option) + " at " + hex(i) + " with length " + hex(length))
3976+ i += length + 2
3977+ return endpoint
3978+
3979+ def DoDhcpWork(self):
3980+ #
3981+ # Discover the wire server via DHCP option 245.
3982+ # And workaround incompatibility with Windows Azure DHCP servers.
3983+ #
3984+ ShortSleep = False # Sleep 1 second before retrying DHCP queries.
3985+ ifname=None
3986+ if not IsWindows():
3987+ Run("iptables -D INPUT -p udp --dport 68 -j ACCEPT")
3988+ Run("iptables -I INPUT -p udp --dport 68 -j ACCEPT")
3989+
3990+ sleepDurations = [0, 5, 10, 30, 60, 60, 60, 60]
3991+ maxRetry = len(sleepDurations)
3992+ lastTry = (maxRetry - 1)
3993+ for retry in range(0, maxRetry):
3994+ try:
3995+ strRetry = str(retry)
3996+ prefix = "DoDhcpWork: try=" + strRetry
3997+ LogIfVerbose(prefix)
3998+ sendData = self.BuildDhcpRequest()
3999+ LogWithPrefixIfVerbose("DHCP request:", HexDump(sendData, len(sendData)))
4000+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
4001+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
4002+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
4003+ missingDefaultRoute = True
4004+ try:
4005+ for line in os.popen("route -n").read().split('\n'):
4006+ if line.startswith("0.0.0.0 "):
4007+ missingDefaultRoute = False
4008+ except:
4009+ pass
4010+ if missingDefaultRoute:
4011+ # This is required because sending after binding to 0.0.0.0 fails with
4012+ # network unreachable when the default gateway is not set up.
4013+ for i in PossibleEthernetInterfaces:
4014+ try:
4015+ if Linux_ioctl_GetIpv4Address(i):
4016+ ifname=i
4017+ except IOError, e:
4018+ pass
4019+ Log("DoDhcpWork: Missing default route - adding broadcast route for DHCP.")
4020+ Run("route add 255.255.255.255 dev " + ifname)
4021+ sock.bind(("0.0.0.0", 68))
4022+ sock.sendto(sendData, ("<broadcast>", 67))
4023+ sock.settimeout(10)
4024+ Log("DoDhcpWork: Setting socket.timeout=10, entering recv")
4025+ receiveBuffer = sock.recv(1024)
4026+ endpoint = self.HandleDhcpResponse(sendData, receiveBuffer)
4027+ if endpoint == None:
4028+ LogIfVerbose("DoDhcpWork: No endpoint found")
4029+ if endpoint != None or retry == lastTry:
4030+ if endpoint != None:
4031+ self.SendData = sendData
4032+ self.DhcpResponse = receiveBuffer
4033+ if retry == lastTry:
4034+ LogIfVerbose("DoDhcpWork: try=" + strRetry)
4035+ return endpoint
4036+ sleepDuration = [sleepDurations[retry % len(sleepDurations)], 1][ShortSleep]
4037+ LogIfVerbose("DoDhcpWork: sleep=" + str(sleepDuration))
4038+ time.sleep(sleepDuration)
4039+ except Exception, e:
4040+ ErrorWithPrefix(prefix, str(e))
4041+ ErrorWithPrefix(prefix, traceback.format_exc())
4042+ finally:
4043+ sock.close()
4044+ if missingDefaultRoute:
4045+ #We added this route - delete it
4046+ Run("route del 255.255.255.255 dev " + ifname)
4047+ Log("DoDhcpWork: Removing broadcast route for DHCP.")
4048+ return None
4049+
4050+ def UpdateAndPublishHostName(self, name):
4051+ # Set hostname locally and publish to iDNS
4052+ Log("Setting host name: " + name)
4053+ UpdateAndPublishHostNameCommon(name)
4054+ for ethernetInterface in PossibleEthernetInterfaces:
4055+ Run("ifdown " + ethernetInterface + " && ifup " + ethernetInterface)
4056+ self.RestoreRoutes()
4057+
4058+ def RestoreRoutes(self):
4059+ if self.SendData != None and self.DhcpResponse != None:
4060+ self.HandleDhcpResponse(self.SendData, self.DhcpResponse)
4061+
4062+ def UpdateGoalState(self):
4063+ goalStateXml = None
4064+ maxRetry = 9
4065+ log = NoLog
4066+ for retry in range(1, maxRetry + 1):
4067+ strRetry = str(retry)
4068+ log("retry UpdateGoalState,retry=" + strRetry)
4069+ goalStateXml = self.HttpGetWithHeaders("/machine/?comp=goalstate")
4070+ if goalStateXml != None:
4071+ break
4072+ log = Log
4073+ time.sleep(retry)
4074+ if not goalStateXml:
4075+ Error("UpdateGoalState failed.")
4076+ return
4077+ Log("Retrieved GoalState from Windows Azure Fabric.")
4078+ self.GoalState = GoalState(self).Parse(goalStateXml)
4079+ return self.GoalState
4080+
4081+ def ReportReady(self):
4082+ counter = (self.HealthReportCounter + 1) % 1000000
4083+ self.HealthReportCounter = counter
4084+ healthReport = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><Health xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GoalStateIncarnation>"
4085+ + self.GoalState.Incarnation
4086+ + "</GoalStateIncarnation><Container><ContainerId>"
4087+ + self.GoalState.ContainerId
4088+ + "</ContainerId><RoleInstanceList><Role><InstanceId>"
4089+ + self.GoalState.RoleInstanceId
4090+ + "</InstanceId><Health><State>Ready</State></Health></Role></RoleInstanceList></Container></Health>")
4091+ a = self.HttpPost("/machine?comp=health", healthReport)
4092+ if a != None:
4093+ return a.getheader("x-ms-latest-goal-state-incarnation-number")
4094+ return None
4095+
4096+ def ReportNotReady(self, status, desc):
4097+ healthReport = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><Health xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><GoalStateIncarnation>"
4098+ + self.GoalState.Incarnation
4099+ + "</GoalStateIncarnation><Container><ContainerId>"
4100+ + self.GoalState.ContainerId
4101+ + "</ContainerId><RoleInstanceList><Role><InstanceId>"
4102+ + self.GoalState.RoleInstanceId
4103+ + "</InstanceId><Health><State>NotReady</State>"
4104+ + "<Details><SubStatus>" + status + "</SubStatus><Description>" + desc + "</Description></Details>"
4105+ + "</Health></Role></RoleInstanceList></Container></Health>")
4106+ a = self.HttpPost("/machine?comp=health", healthReport)
4107+ if a != None:
4108+ return a.getheader("x-ms-latest-goal-state-incarnation-number")
4109+ return None
4110+
4111+ def ReportRoleProperties(self, thumbprint):
4112+ roleProperties = ("<?xml version=\"1.0\" encoding=\"utf-8\"?><RoleProperties><Container>"
4113+ + "<ContainerId>" + self.GoalState.ContainerId + "</ContainerId>"
4114+ + "<RoleInstances><RoleInstance>"
4115+ + "<Id>" + self.GoalState.RoleInstanceId + "</Id>"
4116+ + "<Properties><Property name=\"CertificateThumbprint\" value=\"" + thumbprint + "\" /></Properties>"
4117+ + "</RoleInstance></RoleInstances></Container></RoleProperties>")
4118+ a = self.HttpPost("/machine?comp=roleProperties", roleProperties)
4119+ Log("Posted Role Properties. CertificateThumbprint=" + thumbprint)
4120+ return a
4121+
4122+ def LoadBalancerProbeServer_Shutdown(self):
4123+ if self.LoadBalancerProbeServer != None:
4124+ self.LoadBalancerProbeServer.shutdown()
4125+ self.LoadBalancerProbeServer = None
4126+
4127+ def GenerateTransportCert(self):
4128+ Run(Openssl + " req -x509 -nodes -subj /CN=LinuxTransport -days 32768 -newkey rsa:2048 -keyout TransportPrivate.pem -out TransportCert.pem")
4129+ cert = ""
4130+ for line in GetFileContents("TransportCert.pem").split('\n'):
4131+ if not "CERTIFICATE" in line:
4132+ cert += line.rstrip()
4133+ return cert
4134+
4135+ def Provision(self):
4136+ if IsWindows():
4137+ Log("Skipping Provision on Windows")
4138+ return
4139+ enabled = Config.get("Provisioning.Enabled")
4140+ if enabled != None and enabled.lower().startswith("n"):
4141+ return
4142+ Log("Provisioning image started.")
4143+ type = Config.get("Provisioning.SshHostKeyPairType")
4144+ if type == None:
4145+ type = "rsa"
4146+ regenerateKeys = Config.get("Provisioning.RegenerateSshHostKeyPair")
4147+ if regenerateKeys == None or regenerateKeys.lower().startswith("y"):
4148+ Run("rm -f /etc/ssh/ssh_host_*key*")
4149+ Run("ssh-keygen -N '' -t " + type + " -f /etc/ssh/ssh_host_" + type + "_key")
4150+ ReloadSshd()
4151+ SetFileContents(LibDir + "/provisioned", "")
4152+ dvd = "/dev/hdc"
4153+ if os.path.exists("/dev/sr0"):
4154+ dvd = "/dev/sr0"
4155+ modloaded=False
4156+ if Run("fdisk -l " + dvd + " | grep Disk"):
4157+ # Is it possible to load a module for ata_piix?
4158+ retcode,krn=RunSafe('uname -r')
4159+ if retcode:
4160+ Error("Unable to provision: Failed to call uname -a")
4161+ return "Unable to provision: Failed to mount DVD."
4162+ krn_pth='/lib/modules/'+krn+'/kernel/drivers/ata/ata_piix.ko'
4163+ if not os.path.isfile(krn_pth):
4164+ Error("Unable to provision: Failed to locate ata_piix.ko")
4165+ return "Unable to provision: Failed to mount DVD."
4166+ retcode,output=RunSafe('insmod ' + krn_pth)
4167+ if retcode:
4168+ Error("Unable to provision: Failed to insmod " + krn+pth)
4169+ return "Failed to retrieve provisioning data (0x01)."
4170+ modloaded=True
4171+ Log("Provision: Loaded " + krn_pth + " driver for ATAPI CD-ROM")
4172+ # we have succeeded loading the ata_piix mod
4173+ for i in range(10): # we may have to wait
4174+ if os.path.exists("/dev/sr0"):
4175+ dvd = "/dev/sr0"
4176+ break
4177+ Log("Waiting for DVD - sleeping 1 - "+str(i+1)+" try...")
4178+ time.sleep(1)
4179+ CreateDir("/mnt/cdrom/secure", "root", 0700)
4180+ Run("mount " + dvd + " /mnt/cdrom/secure")
4181+ if not os.path.isfile("/mnt/cdrom/secure/ovf-env.xml"):
4182+ Error("Unable to provision: Missing ovf-env.xml on DVD.")
4183+ return "Failed to retrieve provisioning data (0x02)."
4184+ ovfxml = GetFileContents("/mnt/cdrom/secure/ovf-env.xml")
4185+ SetFileContents("ovf-env.xml", re.sub("<UserPassword>.*?<", "<UserPassword>*<", ovfxml))
4186+ Run("umount /mnt/cdrom/secure")
4187+ if modloaded:
4188+ Run('rmmod ' + krn_pth)
4189+ error = None
4190+ if ovfxml != None:
4191+ Log("Provisioning image using OVF settings in the DVD.")
4192+ ovfobj = OvfEnv().Parse(ovfxml)
4193+ if ovfobj != None:
4194+ error = ovfobj.Process()
4195+ if error :
4196+ Error ("Provisioninig image FAILED " + error)
4197+ return ("Provisioninig image FAILED " + error)
4198+ # This is done here because regenerated SSH host key pairs may be potentially overwritten when processing the ovfxml
4199+ fingerprint = os.popen("ssh-keygen -lf /etc/ssh/ssh_host_" + type + "_key.pub").read().rstrip().split()[1].replace(':','')
4200+ self.ReportRoleProperties(fingerprint)
4201+ delRootPass = Config.get("Provisioning.DeleteRootPassword")
4202+ if delRootPass != None and delRootPass.lower().startswith("y"):
4203+ DeleteRootPassword()
4204+ Log("Provisioning image completed.")
4205+ return error
4206+
4207+ def Run(self):
4208+ if IsLinux():
4209+ SetFileContents("/var/run/waagent.pid", str(os.getpid()) + "\n")
4210+
4211+ if GetIpv4Address() == None:
4212+ Log("Waiting for network.")
4213+ while(GetIpv4Address() == None):
4214+ time.sleep(10)
4215+
4216+ Log("IPv4 address: " + GetIpv4Address())
4217+ Log("MAC address: " + ":".join(["%02X" % Ord(a) for a in GetMacAddress()]))
4218+
4219+ # Consume Entropy in ACPI table provided by Hyper-V
4220+ try:
4221+ SetFileContents("/dev/random", GetFileContents("/sys/firmware/acpi/tables/OEM0"))
4222+ except:
4223+ pass
4224+
4225+ Log("Probing for Windows Azure environment.")
4226+ self.Endpoint = self.DoDhcpWork()
4227+
4228+ if self.Endpoint == None:
4229+ Log("Windows Azure environment not detected.")
4230+ while True:
4231+ time.sleep(60)
4232+
4233+ Log("Discovered Windows Azure endpoint: " + self.Endpoint)
4234+ if not self.CheckVersions():
4235+ Error("Agent.CheckVersions failed")
4236+ sys.exit(1)
4237+
4238+ self.EnvMonitor = EnvMonitor()
4239+
4240+ # Set SCSI timeout on root device
4241+ try:
4242+ timeout = Config.get("OS.RootDeviceScsiTimeout")
4243+ if timeout != None:
4244+ SetFileContents("/sys/block/" + DeviceForIdePort(0) + "/device/timeout", timeout)
4245+ except:
4246+ pass
4247+
4248+ global Openssl
4249+ Openssl = Config.get("OS.OpensslPath")
4250+ if Openssl == None:
4251+ Openssl = "openssl"
4252+
4253+ self.TransportCert = self.GenerateTransportCert()
4254+
4255+ incarnation = None # goalStateIncarnationFromHealthReport
4256+ currentPort = None # loadBalancerProbePort
4257+ goalState = None # self.GoalState, instance of GoalState
4258+ provisioned = os.path.exists(LibDir + "/provisioned")
4259+ program = Config.get("Role.StateConsumer")
4260+ provisionError = None
4261+ lbProbeResponder = True
4262+ setting = Config.get("LBProbeResponder")
4263+ if setting != None and setting.lower().startswith("n"):
4264+ lbProbeResponder = False
4265+ while True:
4266+ if (goalState == None) or (incarnation == None) or (goalState.Incarnation != incarnation):
4267+ goalState = self.UpdateGoalState()
4268+
4269+ if provisioned == False:
4270+ self.ReportNotReady("Provisioning", "Starting")
4271+
4272+ goalState.Process()
4273+
4274+ if provisioned == False:
4275+ provisionError = self.Provision()
4276+ provisioned = True
4277+
4278+ #
4279+ # only one port supported
4280+ # restart server if new port is different than old port
4281+ # stop server if no longer a port
4282+ #
4283+ goalPort = goalState.LoadBalancerProbePort
4284+ if currentPort != goalPort:
4285+ self.LoadBalancerProbeServer_Shutdown()
4286+ currentPort = goalPort
4287+ if currentPort != None and lbProbeResponder == True:
4288+ self.LoadBalancerProbeServer = LoadBalancerProbeServer(currentPort)
4289+ if self.LoadBalancerProbeServer == None :
4290+ lbProbeResponder = False
4291+ Log("Unable to create LBProbeResponder.")
4292+
4293+ if program != None and DiskActivated == True:
4294+ Children.append(subprocess.Popen([program, "Ready"]))
4295+ program = None
4296+
4297+ if goalState.ExpectedState == "Stopped":
4298+ program = Config.get("Role.StateConsumer")
4299+ if program != None:
4300+ Run(program + " Shutdown")
4301+ self.EnvMonitor.ShutdownService()
4302+ self.LoadBalancerProbeServer_Shutdown()
4303+ command = ["/sbin/shutdown -hP now", "shutdown /s /t 5"][IsWindows()]
4304+ Run(command)
4305+ return
4306+
4307+ sleepToReduceAccessDenied = 3
4308+ time.sleep(sleepToReduceAccessDenied)
4309+ if provisionError != None:
4310+ incarnation = self.ReportNotReady("ProvisioningFailed", provisionError)
4311+ else:
4312+ incarnation = self.ReportReady()
4313+ time.sleep(25 - sleepToReduceAccessDenied)
4314+
4315+Init_Suse = """\
4316+#! /bin/sh
4317+
4318+### BEGIN INIT INFO
4319+# Provides: WindowsAzureLinuxAgent
4320+# Required-Start: $network sshd
4321+# Required-Stop: $network sshd
4322+# Default-Start: 3 5
4323+# Default-Stop: 0 1 2 6
4324+# Description: Start the WindowsAzureLinuxAgent
4325+### END INIT INFO
4326+
4327+WAZD_BIN=/usr/sbin/waagent
4328+test -x $WAZD_BIN || exit 5
4329+
4330+case "$1" in
4331+ start)
4332+ echo "Starting WindowsAzureLinuxAgent"
4333+ ## Start daemon with startproc(8). If this fails
4334+ ## the echo return value is set appropriate.
4335+
4336+ startproc -f $WAZD_BIN -daemon
4337+ exit $?
4338+ ;;
4339+ stop)
4340+ echo "Shutting down WindowsAzureLinuxAgent"
4341+ ## Stop daemon with killproc(8) and if this fails
4342+ ## set echo the echo return value.
4343+
4344+ killproc -p /var/run/waagent.pid $WAZD_BIN
4345+ exit $?
4346+ ;;
4347+ try-restart)
4348+ ## Stop the service and if this succeeds (i.e. the
4349+ ## service was running before), start it again.
4350+ $0 status >/dev/null && $0 restart
4351+ ;;
4352+ restart)
4353+ ## Stop the service and regardless of whether it was
4354+ ## running or not, start it again.
4355+ $0 stop
4356+ $0 start
4357+ ;;
4358+ force-reload|reload)
4359+ ;;
4360+ status)
4361+ echo -n "Checking for service WindowsAzureLinuxAgent "
4362+ ## Check status with checkproc(8), if process is running
4363+ ## checkproc will return with exit status 0.
4364+
4365+ checkproc -p $WAZD_PIDFILE $WAZD_BIN
4366+ exit $?
4367+ ;;
4368+ probe)
4369+ ;;
4370+ *)
4371+ echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}"
4372+ exit 1
4373+ ;;
4374+esac
4375+"""
4376+
4377+Init_RedHat = """\
4378+#!/bin/bash
4379+#
4380+# Init file for WindowsAzureLinuxAgent.
4381+#
4382+# chkconfig: 2345 60 80
4383+# description: WindowsAzureLinuxAgent
4384+#
4385+
4386+# source function library
4387+. /etc/rc.d/init.d/functions
4388+
4389+RETVAL=0
4390+FriendlyName="WindowsAzureLinuxAgent"
4391+WAZD_BIN=/usr/sbin/waagent
4392+
4393+start()
4394+{
4395+ echo -n $"Starting $FriendlyName: "
4396+ $WAZD_BIN -daemon &
4397+}
4398+
4399+stop()
4400+{
4401+ echo -n $"Stopping $FriendlyName: "
4402+ killproc -p /var/run/waagent.pid $WAZD_BIN
4403+ RETVAL=$?
4404+ echo
4405+ return $RETVAL
4406+}
4407+
4408+case "$1" in
4409+ start)
4410+ start
4411+ ;;
4412+ stop)
4413+ stop
4414+ ;;
4415+ restart)
4416+ stop
4417+ start
4418+ ;;
4419+ reload)
4420+ ;;
4421+ report)
4422+ ;;
4423+ status)
4424+ status $WAZD_BIN
4425+ RETVAL=$?
4426+ ;;
4427+ *)
4428+ echo $"Usage: $0 {start|stop|restart|status}"
4429+ RETVAL=1
4430+esac
4431+exit $RETVAL
4432+"""
4433+
4434+Init_Ubuntu = """\
4435+#walinuxagent - start Windows Azure agent
4436+
4437+description "walinuxagent"
4438+author "Ben Howard <ben.howard@canonical.com>"
4439+
4440+start on (filesystem and started rsyslog)
4441+
4442+pre-start script
4443+
4444+ WALINUXAGENT_ENABLED=1
4445+ [ -r /etc/default/walinuxagent ] && . /etc/default/walinuxagent
4446+
4447+ if [ "$WALINUXAGENT_ENABLED" != "1" ]; then
4448+ exit 1
4449+ fi
4450+
4451+ if [ ! -x /usr/sbin/waagent ]; then
4452+ exit 1
4453+ fi
4454+
4455+ #Load the udf module
4456+ modprobe -b udf
4457+end script
4458+
4459+exec /usr/sbin/waagent -daemon
4460+"""
4461+
4462+Init_Debian = """\
4463+#!/bin/sh
4464+### BEGIN INIT INFO
4465+# Provides: WindowsAzureLinuxAgent
4466+# Required-Start: $network $syslog
4467+# Required-Stop: $network $syslog
4468+# Should-Start: $network $syslog
4469+# Should-Stop: $network $syslog
4470+# Default-Start: 2 3 4 5
4471+# Default-Stop: 0 1 6
4472+# Short-Description: WindowsAzureLinuxAgent
4473+# Description: WindowsAzureLinuxAgent
4474+### END INIT INFO
4475+
4476+. /lib/lsb/init-functions
4477+
4478+OPTIONS="-daemon"
4479+WAZD_BIN=/usr/sbin/waagent
4480+WAZD_PID=/var/run/waagent.pid
4481+
4482+case "$1" in
4483+ start)
4484+ log_begin_msg "Starting WindowsAzureLinuxAgent..."
4485+ pid=$( pidofproc $WAZD_BIN )
4486+ if [ -n "$pid" ] ; then
4487+ log_begin_msg "Already running."
4488+ log_end_msg 0
4489+ exit 0
4490+ fi
4491+ start-stop-daemon --start --quiet --oknodo --background --exec $WAZD_BIN -- $OPTIONS
4492+ log_end_msg $?
4493+ ;;
4494+
4495+ stop)
4496+ log_begin_msg "Stopping WindowsAzureLinuxAgent..."
4497+ start-stop-daemon --stop --quiet --oknodo --pidfile $WAZD_PID
4498+ ret=$?
4499+ rm -f $WAZD_PID
4500+ log_end_msg $ret
4501+ ;;
4502+ force-reload)
4503+ $0 restart
4504+ ;;
4505+ restart)
4506+ $0 stop
4507+ $0 start
4508+ ;;
4509+ status)
4510+ status_of_proc $WAZD_BIN && exit 0 || exit $?
4511+ ;;
4512+ *)
4513+ log_success_msg "Usage: /etc/init.d/waagent {start|stop|force-reload|restart|status}"
4514+ exit 1
4515+ ;;
4516+esac
4517+
4518+exit 0
4519+"""
4520+
4521+WaagentConf = """\
4522+#
4523+# Windows Azure Linux Agent Configuration
4524+#
4525+
4526+Role.StateConsumer=None # Specified program is invoked with "Ready" or "Shutdown".
4527+ # Shutdown will be initiated only after the program returns. Windows Azure will
4528+ # power off the VM if shutdown is not completed within ?? minutes.
4529+Role.ConfigurationConsumer=None # Specified program is invoked with XML file argument specifying role configuration.
4530+Role.TopologyConsumer=None # Specified program is invoked with XML file argument specifying role topology.
4531+
4532+Provisioning.Enabled=y #
4533+Provisioning.DeleteRootPassword=y # Password authentication for root account will be unavailable.
4534+Provisioning.RegenerateSshHostKeyPair=y # Generate fresh host key pair.
4535+Provisioning.SshHostKeyPairType=rsa # Supported values are "rsa", "dsa" and "ecdsa".
4536+Provisioning.MonitorHostName=y # Monitor host name changes and publish changes via DHCP requests.
4537+
4538+ResourceDisk.Format=y # Format if unformatted. If 'n', resource disk will not be mounted.
4539+ResourceDisk.Filesystem=ext4 #
4540+ResourceDisk.MountPoint=/mnt/resource #
4541+ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
4542+ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
4543+
4544+LBProbeResponder=y # Respond to load balancer probes if requested by Windows Azure.
4545+
4546+Logs.Verbose=n #
4547+
4548+OS.RootDeviceScsiTimeout=300 # Root device timeout in seconds.
4549+OS.OpensslPath=None # If "None", the system default version is used.
4550+"""
4551+
4552+WaagentLogrotate = """\
4553+/var/log/waagent.log {
4554+ monthly
4555+ rotate 6
4556+ notifempty
4557+ missingok
4558+}
4559+"""
4560+
4561+def AddToLinuxKernelCmdline(options):
4562+ if os.path.isfile("/boot/grub/menu.lst"):
4563+ Run("sed -i --follow-symlinks '/kernel/s|$| " + options + " |' /boot/grub/menu.lst")
4564+ filepath = "/etc/default/grub"
4565+ if os.path.isfile(filepath):
4566+ filecontents = GetFileContents(filepath).split('\n')
4567+ current = filter(lambda a: a.startswith("GRUB_CMDLINE_LINUX"), filecontents)
4568+ ReplaceFileContentsAtomic(filepath,
4569+ "\n".join(filter(lambda a: not a.startswith("GRUB_CMDLINE_LINUX"), filecontents))
4570+ + current[0][:-1] + " " + options + "\"\n")
4571+ Run("update-grub")
4572+
4573+def ApplyVNUMAWorkaround():
4574+ VersionParts = platform.release().replace('-', '.').split('.')
4575+ if int(VersionParts[0]) > 2:
4576+ return
4577+ if int(VersionParts[1]) > 6:
4578+ return
4579+ if int(VersionParts[2]) > 37:
4580+ return
4581+ AddToLinuxKernelCmdline("numa=off")
4582+ # TODO: This is not ideal for offline installation.
4583+ print("Your kernel version " + platform.release() + " has a NUMA-related bug: NUMA has been disabled.")
4584+
4585+def RevertVNUMAWorkaround():
4586+ print("Automatic reverting of GRUB configuration is not yet supported. Please edit by hand.")
4587+
4588+def Install():
4589+ if IsWindows():
4590+ print("ERROR: -install invalid for Windows.")
4591+ return 1
4592+ os.chmod(sys.argv[0], 0755)
4593+ SwitchCwd()
4594+ requiredDeps = [ "/sbin/route", "/sbin/shutdown" ]
4595+ if IsDebian():
4596+ requiredDeps += [ "/usr/sbin/update-rc.d" ]
4597+ if IsSuse():
4598+ requiredDeps += [ "/sbin/insserv" ]
4599+ for a in requiredDeps:
4600+ if not os.path.isfile(a):
4601+ Error("Missing required dependency: " + a)
4602+ return 1
4603+ missing = False
4604+ for a in [ "ssh-keygen", "useradd", "openssl", "sfdisk",
4605+ "fdisk", "mkfs", "chpasswd", "sed", "grep", "sudo" ]:
4606+ if Run("which " + a + " > /dev/null 2>&1"):
4607+ Warn("Missing dependency: " + a)
4608+ missing = True
4609+ if missing == True:
4610+ Warn("Please resolve missing dependencies listed for full functionality.")
4611+ if UsesRpm():
4612+ if not Run("rpm --quiet -q NetworkManager"):
4613+ Error(GuestAgentLongName + " is not compatible with NetworkManager.")
4614+ return 1
4615+ if Run("rpm --quiet -q python-pyasn1"):
4616+ Error(GuestAgentLongName + " requires python-pyasn1.")
4617+ return 1
4618+ if UsesDpkg() and not Run("dpkg-query -s network-manager >/dev/null 2>&1"):
4619+ Error(GuestAgentLongName + " is not compatible with network-manager.")
4620+ return 1
4621+ for a in RulesFiles:
4622+ if os.path.isfile(a):
4623+ if os.path.isfile(GetLastPathElement(a)):
4624+ os.remove(GetLastPathElement(a))
4625+ shutil.move(a, ".")
4626+ Warn("Moved " + a + " -> " + LibDir + "/" + GetLastPathElement(a) )
4627+
4628+ if IsUbuntu():
4629+ # Support for Ubuntu's upstart configuration
4630+ filename="waagent.conf"
4631+ filepath = "/etc/init/" + filename
4632+ SetFileContents(filepath, Init_Ubuntu)
4633+ os.chmod(filepath, 0644)
4634+
4635+ else:
4636+ # Regular init.d configurations
4637+ filename = "waagent"
4638+ filepath = "/etc/init.d/" + filename
4639+ distro = IsRedHat() + IsDebian() * 2 + IsSuse() * 3
4640+ if distro == 0:
4641+ Error("Unable to detect Linux Distribution.")
4642+ return 1
4643+ init = [[Init_RedHat, "chkconfig --add " + filename],
4644+ [Init_Debian, "update-rc.d " + filename + " defaults"],
4645+ [Init_Suse, "insserv " + filename]][distro - 1]
4646+ SetFileContents(filepath, init[0])
4647+ os.chmod(filepath, 0755)
4648+ Run(init[1])
4649+ if os.path.isfile("/etc/waagent.conf"):
4650+ try:
4651+ os.remove("/etc/waagent.conf.old")
4652+ except:
4653+ pass
4654+ try:
4655+ os.rename("/etc/waagent.conf", "/etc/waagent.conf.old")
4656+ Warn("Existing /etc/waagent.conf has been renamed to /etc/waagent.conf.old")
4657+ except:
4658+ pass
4659+ SetFileContents("/etc/waagent.conf", WaagentConf)
4660+ SetFileContents("/etc/logrotate.d/waagent", WaagentLogrotate)
4661+ filepath = "/etc/ssh/sshd_config"
4662+ ReplaceFileContentsAtomic(filepath, "\n".join(filter(lambda a: not
4663+ a.startswith("ClientAliveInterval"),
4664+ GetFileContents(filepath).split('\n'))) + "ClientAliveInterval 180\n")
4665+ Log("Configured SSH client probing to keep connections alive.")
4666+ ApplyVNUMAWorkaround()
4667+ return 0
4668+
4669+def Uninstall():
4670+ if IsWindows():
4671+ print("ERROR: -uninstall invalid for windows, see waagent_service.exe")
4672+ return 1
4673+ SwitchCwd()
4674+ for a in RulesFiles:
4675+ if os.path.isfile(GetLastPathElement(a)):
4676+ try:
4677+ shutil.move(GetLastPathElement(a), a)
4678+ Warn("Moved " + LibDir + "/" + GetLastPathElement(a) + " -> " + a )
4679+ except:
4680+ pass
4681+ filename = "waagent"
4682+ a = IsRedHat() + IsDebian() * 2 + IsSuse() * 3
4683+ if a == 0:
4684+ Error("Unable to detect Linux Distribution.")
4685+ return 1
4686+ Run("service " + filename + " stop")
4687+ cmd = ["chkconfig --del " + filename,
4688+ "update-rc.d -f " + filename + " remove",
4689+ "insserv -r " + filename][a - 1]
4690+ Run(cmd)
4691+ for f in os.listdir(LibDir) + ["/etc/init.d/" + filename, "/etc/waagent.conf", "/etc/logrotate.d/waagent", "/etc/sudoers.d/waagent"]:
4692+ try:
4693+ os.remove(f)
4694+ except:
4695+ pass
4696+ RevertVNUMAWorkaround()
4697+ return 0
4698+
4699+def DeleteRootPassword():
4700+ filepath="/etc/shadow"
4701+ ReplaceFileContentsAtomic(filepath, "root:*LOCK*:14600::::::\n" + "\n".join(filter(lambda a: not
4702+ a.startswith("root:"),
4703+ GetFileContents(filepath).split('\n'))))
4704+ os.chmod(filepath, 0000)
4705+ if IsRedHat():
4706+ Run("chcon system_u:object_r:shadow_t:s0 " + filepath)
4707+ Log("Root password deleted.")
4708+
4709+def Deprovision(force, deluser):
4710+ if IsWindows():
4711+ Run(os.environ["windir"] + "\\system32\\sysprep\\sysprep.exe /generalize")
4712+ return 0
4713+
4714+ SwitchCwd()
4715+ ovfxml = GetFileContents("ovf-env.xml")
4716+ ovfobj = None
4717+ if ovfxml != None:
4718+ ovfobj = OvfEnv().Parse(ovfxml)
4719+
4720+ print("WARNING! The waagent service will be stopped.")
4721+ print("WARNING! All SSH host key pairs will be deleted.")
4722+ if IsUbuntu():
4723+ print("WARNING! Nameserver configuration in /etc/resolvconf/resolv.conf.d/{tail,originial} will be deleted.")
4724+ else:
4725+ print("WARNING! Nameserver configuration in /etc/resolv.conf will be deleted.")
4726+ print("WARNING! Cached DHCP leases will be deleted.")
4727+
4728+ delRootPass = Config.get("Provisioning.DeleteRootPassword")
4729+ if delRootPass != None and delRootPass.lower().startswith("y"):
4730+ print("WARNING! root password will be disabled. You will not be able to login as root.")
4731+
4732+ if ovfobj != None and deluser == True:
4733+ print("WARNING! " + ovfobj.UserName + " account and entire home directory will be deleted.")
4734+
4735+ if force == False and not raw_input('Do you want to proceed (y/n)? ').startswith('y'):
4736+ return 1
4737+
4738+ Run("service waagent stop")
4739+
4740+ if deluser == True:
4741+ DeleteAccount(ovfobj.UserName)
4742+
4743+ # Remove SSH host keys
4744+ regenerateKeys = Config.get("Provisioning.RegenerateSshHostKeyPair")
4745+ if regenerateKeys == None or regenerateKeys.lower().startswith("y"):
4746+ Run("rm -f /etc/ssh/ssh_host_*key*")
4747+
4748+ # Remove root password
4749+ if delRootPass != None and delRootPass.lower().startswith("y"):
4750+ DeleteRootPassword()
4751+
4752+ # Remove distribution specific networking configuration
4753+
4754+ UpdateAndPublishHostNameCommon("localhost.localdomain")
4755+
4756+ # RedHat, Suse, Debian
4757+ for a in VarLibDhcpDirectories:
4758+ Run("rm -f " + a + "/*")
4759+
4760+ # Clear LibDir, remove nameserver and root bash history
4761+ fileBlackList = [ "/root/.bash_history", "/var/log/waagent.log" ]
4762+
4763+ if IsUbuntu():
4764+ # Ubuntu uses resolv.conf by default, so removing /etc/resolv.conf will
4765+ # break resolvconf. Therefore, we check to see if resolvconf is in use,
4766+ # and if so, we remove the resolvconf artifacts.
4767+
4768+ Log("Deprovision: Ubuntu specific resolv.conf behavior selected.")
4769+ if os.path.realpath('/etc/resolv.conf') != '/run/resolvconf/resolv.conf':
4770+ Log("resolvconf is not configured. Removing /etc/resolv.conf")
4771+ fileBlackList.append('/etc/resolv.conf')
4772+ else:
4773+ Log("resolvconf is enabled; leaving /etc/resolv.conf intact")
4774+ resolvConfD = '/etc/resolvconf/resolv.conf.d/'
4775+ fileBlackList.extend([resolvConfD + 'tail', resolvConfD + 'originial' ])
4776+ else:
4777+ fileBlackList.extend(os.listdir(LibDir) + ['/etc/resolv.conf'])
4778+
4779+ for f in os.listdir(LibDir) + fileBlackList:
4780+ try:
4781+ os.remove(f)
4782+ except:
4783+ pass
4784+ return 0
4785+
4786+def SwitchCwd():
4787+ if not IsWindows():
4788+ CreateDir(LibDir, "root", 0700)
4789+ os.chdir(LibDir)
4790+
4791+def Usage():
4792+ print("usage: " + sys.argv[0] + " [-verbose] [-force] [-help|-install|-uninstall|-deprovision[+user]|-version|-serialconsole|-daemon]")
4793+ return 0
4794+
4795+if GuestAgentVersion == "":
4796+ print("WARNING! This is a non-standard agent that does not include a valid version string.")
4797+if IsLinux() and not DetectLinuxDistro():
4798+ print("WARNING! Unable to detect Linux distribution. Some functionality may be broken.")
4799+
4800+if len(sys.argv) == 1:
4801+ sys.exit(Usage())
4802+
4803+args = []
4804+force = False
4805+for a in sys.argv[1:]:
4806+ if re.match("^([-/]*)(help|usage|\?)", a):
4807+ sys.exit(Usage())
4808+ elif re.match("^([-/]*)verbose", a):
4809+ Verbose = True
4810+ elif re.match("^([-/]*)force", a):
4811+ force = True
4812+ elif re.match("^([-/]*)(setup|install)", a):
4813+ sys.exit(Install())
4814+ elif re.match("^([-/]*)(uninstall)", a):
4815+ sys.exit(Uninstall())
4816+ else:
4817+ args.append(a)
4818+
4819+Config = ConfigurationProvider()
4820+
4821+verbose = Config.get("Logs.Verbose")
4822+if verbose != None and verbose.lower().startswith("y"):
4823+ Verbose = True
4824+
4825+daemon = False
4826+for a in args:
4827+ if re.match("^([-/]*)deprovision\+user", a):
4828+ sys.exit(Deprovision(force, True))
4829+ elif re.match("^([-/]*)deprovision", a):
4830+ sys.exit(Deprovision(force, False))
4831+ elif re.match("^([-/]*)daemon", a):
4832+ daemon = True
4833+ elif re.match("^([-/]*)version", a):
4834+ print(GuestAgentVersion + " running on " + LinuxDistro)
4835+ sys.exit(0)
4836+ elif re.match("^([-/]*)serialconsole", a):
4837+ AddToLinuxKernelCmdline("console=ttyS0 earlyprintk=ttyS0")
4838+ Log("Configured kernel to use ttyS0 as the boot console.")
4839+ sys.exit(0)
4840+ else:
4841+ print("Invalid command line parameter:" + a)
4842+ sys.exit(1)
4843+
4844+if daemon == False:
4845+ sys.exit(Usage())
4846+
4847+try:
4848+ SwitchCwd()
4849+ Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
4850+ if IsLinux():
4851+ Log("Linux Distribution Detected : " + LinuxDistro)
4852+ WaAgent = Agent()
4853+ WaAgent.Run()
4854+except Exception, e:
4855+ Error(traceback.format_exc())
4856+ Error("Exception: " + str(e))
4857+ sys.exit(1)
4858
4859=== removed directory '.pc/001_ubuntu_agent_startup.patch'
4860=== removed file '.pc/001_ubuntu_agent_startup.patch/waagent'
4861--- .pc/001_ubuntu_agent_startup.patch/waagent 2012-06-22 09:10:22 +0000
4862+++ .pc/001_ubuntu_agent_startup.patch/waagent 1970-01-01 00:00:00 +0000
4863@@ -1,2395 +0,0 @@
4864-#!/usr/bin/python
4865-#
4866-# Windows Azure Linux Agent
4867-#
4868-# Copyright 2012 Microsoft Corporation
4869-#
4870-# Licensed under the Apache License, Version 2.0 (the "License");
4871-# you may not use this file except in compliance with the License.
4872-# You may obtain a copy of the License at
4873-#
4874-# http://www.apache.org/licenses/LICENSE-2.0
4875-#
4876-# Unless required by applicable law or agreed to in writing, software
4877-# distributed under the License is distributed on an "AS IS" BASIS,
4878-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4879-# See the License for the specific language governing permissions and
4880-# limitations under the License.
4881-#
4882-# Requires Python 2.4+ and Openssl 1.0+
4883-#
4884-# Implements parts of RFC 2131, 1541, 1497 and
4885-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
4886-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
4887-#
4888-
4889-import array
4890-import base64
4891-import httplib
4892-import os
4893-import os.path
4894-import platform
4895-import pwd
4896-import re
4897-import shutil
4898-import socket
4899-import SocketServer
4900-import struct
4901-import sys
4902-import tempfile
4903-import textwrap
4904-import threading
4905-import time
4906-import traceback
4907-import xml.dom.minidom
4908-
4909-GuestAgentName = "WALinuxAgent"
4910-GuestAgentLongName = "Windows Azure Linux Agent"
4911-GuestAgentVersion = "rd_wala.120504-1323"
4912-ProtocolVersion = "2011-12-31"
4913-
4914-Config = None
4915-LinuxDistro = "UNKNOWN"
4916-Verbose = False
4917-WaAgent = None
4918-DiskActivated = False
4919-Openssl = "openssl"
4920-
4921-PossibleEthernetInterfaces = ["seth0", "seth1", "eth0", "eth1"]
4922-RulesFiles = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
4923- "/etc/udev/rules.d/70-persistent-net.rules" ]
4924-VarLibDhcpDirectories = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"]
4925-EtcDhcpClientConfFiles = ["/etc/dhcp/dhclient.conf", "/etc/dhcp3/dhclient.conf"]
4926-LibDir = "/var/lib/waagent"
4927-
4928-# This lets us index into a string or an array of integers transparently.
4929-def Ord(a):
4930- if type(a) == type("a"):
4931- a = ord(a)
4932- return a
4933-
4934-def IsWindows():
4935- return (platform.uname()[0] == "Windows")
4936-
4937-def IsLinux():
4938- return (platform.uname()[0] == "Linux")
4939-
4940-def DetectLinuxDistro():
4941- global LinuxDistro
4942- if os.path.isfile("/etc/redhat-release"):
4943- LinuxDistro = "RedHat"
4944- return True
4945- if os.path.isfile("/etc/lsb-release") and "Ubuntu" in GetFileContents("/etc/lsb-release"):
4946- LinuxDistro = "Ubuntu"
4947- return True
4948- if os.path.isfile("/etc/debian_version"):
4949- LinuxDistro = "Debian"
4950- return True
4951- if os.path.isfile("/etc/SuSE-release"):
4952- LinuxDistro = "Suse"
4953- return True
4954- return False
4955-
4956-def IsRedHat():
4957- return "RedHat" in LinuxDistro
4958-
4959-def IsUbuntu():
4960- return "Ubuntu" in LinuxDistro
4961-
4962-def IsDebian():
4963- return IsUbuntu() or "Debian" in LinuxDistro
4964-
4965-def IsSuse():
4966- return "Suse" in LinuxDistro
4967-
4968-def UsesRpm():
4969- return IsRedHat() or IsSuse()
4970-
4971-def UsesDpkg():
4972- return IsDebian()
4973-
4974-def GetLastPathElement(path):
4975- return path.rsplit('/', 1)[1]
4976-
4977-def GetFileContents(filepath):
4978- file = None
4979- try:
4980- file = open(filepath)
4981- except:
4982- return None
4983- if file == None:
4984- return None
4985- try:
4986- return file.read()
4987- finally:
4988- file.close()
4989-
4990-def SetFileContents(filepath, contents):
4991- file = open(filepath, "w")
4992- try:
4993- file.write(contents)
4994- finally:
4995- file.close()
4996-
4997-def AppendFileContents(filepath, contents):
4998- file = open(filepath, "a")
4999- try:
5000- file.write(contents)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: