Merge lp:~louis/ubuntu/precise/walinuxagent/walinuxagent-lp1079897 into lp:ubuntu/precise-proposed/walinuxagent

Proposed by Louis Bouchard
Status: Merged
Merge reported by: James Page
Merged at revision: not available
Proposed branch: lp:~louis/ubuntu/precise/walinuxagent/walinuxagent-lp1079897
Merge into: lp:ubuntu/precise-proposed/walinuxagent
Diff against target: 5628 lines (+269/-5041)
19 files modified
.pc/000_ubuntu_init_resolvconf.patch/waagent (+0/-2335)
.pc/001_ubuntu_agent_startup.patch/waagent (+0/-2395)
.pc/applied-patches (+0/-2)
Changelog (+12/-0)
debian/changelog (+34/-0)
debian/control (+10/-0)
debian/patches/000_resolv-conf.patch (+32/-0)
debian/patches/000_ubuntu_init_resolvconf.patch (+0/-153)
debian/patches/001-strip-init-d.patch (+36/-0)
debian/patches/001_ubuntu_agent_startup.patch (+0/-22)
debian/patches/series (+2/-2)
debian/postinst (+7/-2)
debian/preinst (+16/-0)
debian/prerm (+2/-3)
debian/rules (+3/-4)
debian/upstart (+24/-0)
debian/walinuxagent-data-saver.lintian-overrides (+10/-0)
debian/walinuxagent-data-saver.preinst (+16/-0)
waagent (+65/-123)
To merge this branch: bzr merge lp:~louis/ubuntu/precise/walinuxagent/walinuxagent-lp1079897
Reviewer Review Type Date Requested Status
Ubuntu Development Team Pending
Review via email: mp+136698@code.launchpad.net

Description of the change

Backport of the fix for LP: #1079897 from Raring

To post a comment you must log in.

Preview Diff

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

Subscribers

People subscribed via source and target branches