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

Proposed by Ben Howard
Status: Rejected
Rejected by: James Page
Proposed branch: lp:~darkmuggle-deactivatedaccount/ubuntu/precise/walinuxagent/lp1077148
Merge into: lp:ubuntu/precise-proposed/walinuxagent
Diff against target: 7775 lines (+2678/-4819) (has conflicts)
14 files modified
.pc/.quilt_patches (+0/-1)
.pc/.quilt_series (+0/-1)
.pc/.version (+0/-1)
.pc/000_resolv-conf.patch/waagent (+0/-2344)
.pc/000_use_package_upstart.patch/waagent (+2473/-0)
.pc/001-strip-init-d.patch/waagent (+0/-2363)
.pc/applied-patches (+0/-2)
Changelog (+13/-0)
debian/changelog (+42/-7)
debian/patches/000_resolv-conf.patch (+0/-32)
debian/patches/000_use_package_upstart.patch (+16/-0)
debian/patches/001-strip-init-d.patch (+0/-36)
debian/patches/series (+0/-2)
waagent (+134/-30)
Text conflict in debian/changelog
To merge this branch: bzr merge lp:~darkmuggle-deactivatedaccount/ubuntu/precise/walinuxagent/lp1077148
Reviewer Review Type Date Requested Status
James Page Needs Fixing
Review via email: mp+139715@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 :

Version number is incorrect:

walinuxagent (1.1-0ubuntu2~12.04.1ubuntu1) precise-proposed; urgency=high

->

walinuxagent (1.2-0ubuntu2~12.04.1) precise-proposed; urgency=high

Revision history for this message
James Page (james-page) :
review: Needs Fixing
8. By Ben Howard

Correction of version numbering

Unmerged revisions

8. By Ben Howard

Correction of version numbering

7. By Ben Howard

Upgrade to version 1.2 (LP: #1077148).

6. By Ben Howard

Upgrade base package to version 1.2

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

Subscribers

People subscribed via source and target branches

to all changes: