Refactor protocol detection code

This commit is contained in:
Yue Zhang
2015-11-03 13:31:47 +08:00
parent 2a27845aa5
commit c94279bfac
30 changed files with 388 additions and 364 deletions
@@ -21,6 +21,9 @@ import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction
class CoreOSDeprovisionHandler(DeprovisionHandler):
def __init__(self, handlers):
self.handlers = handlers
def setup(self, deluser):
warnings, actions = super(CoreOSDeprovisionHandler, self).setup(deluser)
warnings.append("WARNING! /etc/machine-id will be removed.")
@@ -23,5 +23,5 @@ from azurelinuxagent.distro.default.handlerFactory import DefaultHandlerFactory
class CoreOSHandlerFactory(DefaultHandlerFactory):
def __init__(self):
super(CoreOSHandlerFactory, self).__init__()
self.deprovision_handler = CoreOSDeprovisionHandler()
self.deprovision_handler = CoreOSDeprovisionHandler(self)
@@ -35,6 +35,8 @@ class DeprovisionAction(object):
self.func(*self.args, **self.kwargs)
class DeprovisionHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def del_root_password(self, warnings, actions):
warnings.append("WARNING! root password will be disabled. "
+6 -4
View File
@@ -23,6 +23,7 @@ import threading
import time
import azurelinuxagent.logger as logger
import azurelinuxagent.conf as conf
import azurelinuxagent.protocol.dhcp as dhcp
from azurelinuxagent.utils.osutil import OSUTIL
class EnvHandler(object):
@@ -34,7 +35,8 @@ class EnvHandler(object):
If new scsi disk found, set
"""
def __init__(self, handlers):
self.monitor = EnvMonitor(handlers.dhcp_handler)
self.handlers = handlers
self.monitor = EnvMonitor()
def start(self):
self.monitor.start()
@@ -44,14 +46,14 @@ class EnvHandler(object):
class EnvMonitor(object):
def __init__(self, dhcp_handler):
self.dhcp_handler = dhcp_handler
def __init__(self):
self.stopped = True
self.hostname = None
self.dhcpid = None
self.server_thread=None
def start(self):
self.dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp()
if not self.stopped:
logger.info("Stop existing env monitor service.")
self.stop()
@@ -102,7 +104,7 @@ class EnvMonitor(object):
if newpid is not None and newpid != self.dhcpid:
logger.info("EnvMonitor: Detected dhcp client restart. "
"Restoring routing table.")
self.dhcp_handler.conf_routes()
self.dhcp_resp.conf_routes()
self.dhcpid = newpid
def stop(self):
+6 -3
View File
@@ -25,7 +25,8 @@ import shutil
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.protocol as prot
from azurelinuxagent.protocol.factory import PROT_FACTORY
import azurelinuxagent.protocol.common as prot
from azurelinuxagent.metadata import AGENT_VERSION
from azurelinuxagent.event import add_event, WALAEventOperation
from azurelinuxagent.exception import ExtensionError
@@ -146,10 +147,12 @@ class ExtHandlerState(object):
class ExtHandlersHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def process(self):
try:
protocol = prot.FACTORY.get_default_protocol()
protocol = PROT_FACTORY.get_protocol()
ext_handlers = protocol.get_ext_handlers()
except prot.ProtocolError as e:
add_event(name="WALA", is_success=False, message = text(e))
@@ -195,7 +198,7 @@ class ExtHandlersHandler(object):
if handler.ext_status is not None:
try:
protocol = prot.FACTORY.get_default_protocol()
protocol = PROT_FACTORY.get_protocol()
protocol.report_ext_status(handler.name, handler.ext.name,
handler.ext_status)
except prot.ProtocolError as e:
@@ -16,25 +16,23 @@
#
# Requires Python 2.4+ and Openssl 1.0+
#
from .init import InitHandler
from .run import MainHandler
from .scvmm import ScvmmHandler
from .dhcp import DhcpHandler
from .env import EnvHandler
from .provision import ProvisionHandler
from .resourceDisk import ResourceDiskHandler
from .extension import ExtHandlersHandler
from .deprovision import DeprovisionHandler
from azurelinuxagent.distro.default.init import InitHandler
from azurelinuxagent.distro.default.run import MainHandler
from azurelinuxagent.distro.default.scvmm import ScvmmHandler
from azurelinuxagent.distro.default.env import EnvHandler
from azurelinuxagent.distro.default.provision import ProvisionHandler
from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler
from azurelinuxagent.distro.default.extension import ExtHandlersHandler
from azurelinuxagent.distro.default.deprovision import DeprovisionHandler
class DefaultHandlerFactory(object):
def __init__(self):
self.init_handler = InitHandler()
self.init_handler = InitHandler(self)
self.main_handler = MainHandler(self)
self.scvmm_handler = ScvmmHandler()
self.dhcp_handler = DhcpHandler()
self.scvmm_handler = ScvmmHandler(self)
self.env_handler = EnvHandler(self)
self.provision_handler = ProvisionHandler()
self.resource_disk_handler = ResourceDiskHandler()
self.ext_handlers_handler = ExtHandlersHandler()
self.deprovision_handler = DeprovisionHandler()
self.provision_handler = ProvisionHandler(self)
self.resource_disk_handler = ResourceDiskHandler(self)
self.ext_handlers_handler = ExtHandlersHandler(self)
self.deprovision_handler = DeprovisionHandler(self)
+3
View File
@@ -25,6 +25,9 @@ import azurelinuxagent.utils.fileutil as fileutil
class InitHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def init(self, verbose):
#Init stdout log
level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO
+47 -46
View File
@@ -24,9 +24,10 @@ import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.conf as conf
from azurelinuxagent.event import add_event, WALAEventOperation
from azurelinuxagent.exception import *
from azurelinuxagent.exception import ProvisionError
from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
import azurelinuxagent.protocol as prot
from azurelinuxagent.protocol.factory import PROT_FACTORY, ProvisionStatus, \
ProtocolError
import azurelinuxagent.protocol.ovfenv as ovf
import azurelinuxagent.utils.shellutil as shellutil
import azurelinuxagent.utils.fileutil as fileutil
@@ -35,58 +36,65 @@ CUSTOM_DATA_FILE="CustomData"
class ProvisionHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def report_event(self, message, is_success=False):
add_event(name="WALA", message=message, is_success=is_success,
op=WALAEventOperation.Provision)
def report_not_ready(self, protocol, sub_status, description):
status = ProvisionStatus(status="NotReady", subStatus=sub_status,
description=description)
try:
protocol.report_provision_status(status)
except ProtocolError as e:
self.report_event(text(e))
def report_ready(self, protocol, thumbprint=None):
status = ProvisionStatus(status="Ready")
status.properties.certificateThumbprint = thumbprint
try:
protocol.report_provision_status(status)
except ProtocolError as e:
self.report_event(text(e))
def process(self):
#If provision is not enabled, return
if not conf.get_switch("Provisioning.Enabled", True):
logger.info("Provisioning is disabled. Skip.")
return
return
provisioned = os.path.join(OSUTIL.get_lib_dir(), "provisioned")
if os.path.isfile(provisioned):
return
logger.info("run provision handler.")
protocol = prot.FACTORY.get_default_protocol()
logger.info("Run provision handler.")
logger.info("Copy ovf-env.xml.")
try:
status = prot.ProvisionStatus(status="NotReady",
subStatus="Provisioning",
description="Starting")
try:
protocol.report_provision_status(status)
except prot.ProtocolError as e:
add_event(name="WALA", is_success=False, message=text(e),
op=WALAEventOperation.Provision)
self.provision()
ovfenv = ovf.copy_ovf_env()
except ProtocolError as e:
self.report_event("Failed to copy ovf-env.xml: {0}".format(e))
return
protocol = PROT_FACTORY.detect_protocol_by_file()
self.report_not_ready(protocol, "Provisioning", "Starting")
try:
logger.info("Start provisioning")
self.provision(ovfenv)
fileutil.write_file(provisioned, "")
thumbprint = self.reg_ssh_host_key()
logger.info("Finished provisioning")
status = prot.ProvisionStatus(status="Ready")
status.properties.certificateThumbprint = thumbprint
try:
protocol.report_provision_status(status)
except prot.ProtocolError as pe:
add_event(name="WALA", is_success=False, message=text(pe),
op=WALAEventOperation.Provision)
add_event(name="WALA", is_success=True, message="",
op=WALAEventOperation.Provision)
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
status = prot.ProvisionStatus(status="NotReady",
subStatus="ProvisioningFailed",
description= text(e))
try:
protocol.report_provision_status(status)
except prot.ProtocolError as pe:
add_event(name="WALA", is_success=False, message=text(pe),
op=WALAEventOperation.Provision)
add_event(name="WALA", is_success=False, message=text(e),
op=WALAEventOperation.Provision)
self.report_not_ready(protocol, "ProvisioningFailed", text(e))
self.report_event(text(e))
return
self.report_ready(protocol, thumbprint)
self.report_event("Provision succeed", is_success=True)
def reg_ssh_host_key(self):
keypair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa")
if conf.get_switch("Provisioning.RegenerateSshHostKeyPair"):
@@ -105,14 +113,7 @@ class ProvisionHandler(object):
raise ProvisionError(("Failed to generate ssh host key: "
"ret={0}, out= {1}").format(ret[0], ret[1]))
def provision(self):
logger.info("Copy ovf-env.xml.")
try:
ovfenv = ovf.copy_ovf_env()
except prot.ProtocolError as e:
raise ProvisionError("Failed to copy ovf-env.xml: {0}".format(e))
def provision(self, ovfenv):
logger.info("Handle ovf-env.xml.")
try:
logger.info("Set host name.")
@@ -41,6 +41,8 @@ For additional details to please refer to the MSDN documentation at : http://msd
"""
class ResourceDiskHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def start_activate_resource_disk(self):
disk_thread = threading.Thread(target = self.run)
+8 -9
View File
@@ -28,7 +28,8 @@ from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \
DISTRO_FULL_NAME, PY_VERSION_MAJOR, \
PY_VERSION_MINOR, PY_VERSION_MICRO
import azurelinuxagent.event as event
import azurelinuxagent.protocol as prot
import azurelinuxagent.protocol.dhcp as dhcp
from azurelinuxagent.protocol.factory import PROT_FACTORY
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.fileutil as fileutil
@@ -43,27 +44,25 @@ class MainHandler(object):
logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR,
PY_VERSION_MICRO)
event.enable_unhandled_err_dump("Azure Linux Agent")
event.enable_unhandled_err_dump(AGENT_LONG_NAME)
fileutil.write_file(OSUTIL.get_agent_pid_file_path(), text(os.getpid()))
if conf.get_switch("DetectScvmmEnv", False):
if self.handlers.scvmm_handler.detect_scvmm_env():
return
self.handlers.dhcp_handler.probe()
prot.detect_default_protocol()
event.EventMonitor().start()
PROT_FACTORY.wait_for_network()
self.handlers.provision_handler.process()
PROT_FACTORY.detect_protocol()
if conf.get_switch("ResourceDisk.Format", False):
self.handlers.resource_disk_handler.start_activate_resource_disk()
event.EventMonitor().start()
self.handlers.env_handler.start()
protocol = prot.FACTORY.get_default_protocol()
while True:
#Handle extensions
self.handlers.ext_handlers_handler.process()
+2
View File
@@ -26,6 +26,8 @@ VMM_CONF_FILE_NAME = "linuxosconfiguration.xml"
VMM_STARTUP_SCRIPT_NAME= "install"
class ScvmmHandler(object):
def __init__(self, handlers):
self.handlers = handlers
def detect_scvmm_env(self):
logger.info("Detecting Microsoft System Center VMM Environment")
@@ -33,6 +33,9 @@ def del_resolv():
class UbuntuDeprovisionHandler(DeprovisionHandler):
def __init__(self, handlers):
self.handlers = handlers
def setup(self, deluser):
warnings, actions = super(UbuntuDeprovisionHandler, self).setup(deluser)
warnings.append("WARNING! Nameserver configuration in "
@@ -24,6 +24,6 @@ from azurelinuxagent.distro.default.handlerFactory import DefaultHandlerFactory
class UbuntuHandlerFactory(DefaultHandlerFactory):
def __init__(self):
super(UbuntuHandlerFactory, self).__init__()
self.provision_handler = UbuntuProvisionHandler()
self.deprovision_handler = UbuntuDeprovisionHandler()
self.provision_handler = UbuntuProvisionHandler(self)
self.deprovision_handler = UbuntuDeprovisionHandler(self)
+33 -23
View File
@@ -22,9 +22,10 @@ import time
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.conf as conf
import azurelinuxagent.protocol as prot
from azurelinuxagent.protocol.factory import PROT_FACTORY
import azurelinuxagent.protocol.ovfenv as ovf
from azurelinuxagent.event import add_event, WALAEventOperation
from azurelinuxagent.exception import *
from azurelinuxagent.exception import ProvisionError
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.shellutil as shellutil
import azurelinuxagent.utils.fileutil as fileutil
@@ -34,6 +35,9 @@ from azurelinuxagent.distro.default.provision import ProvisionHandler
On ubuntu image, provision could be disabled.
"""
class UbuntuProvisionHandler(ProvisionHandler):
def __init__(self, handlers):
self.handlers = handlers
def process(self):
#If provision is enabled, run default provision handler
if conf.get_switch("Provisioning.Enabled", False):
@@ -45,37 +49,43 @@ class UbuntuProvisionHandler(ProvisionHandler):
if os.path.isfile(provisioned):
return
logger.info("Waiting cloud-init to finish provisioning.")
protocol = prot.FACTORY.get_default_protocol()
logger.info("Waiting cloud-init to copy ovf-env.xml.")
self.wait_for_ovfenv()
protocol = PROT_FACTORY.detect_protocol_by_file()
self.report_not_ready(protocol, "Provisioning", "Starting")
try:
logger.info("Wait for ssh host key to be generated.")
thumbprint = self.wait_for_ssh_host_key()
fileutil.write_file(provisioned, "")
logger.info("Finished provisioning")
status = prot.ProvisionStatus(status="Ready")
status.properties.certificateThumbprint = thumbprint
try:
protocol.report_provision_status(status)
except prot.ProtocolError as pe:
add_event(name="WALA", is_success=False, message=text(pe),
op=WALAEventOperation.Provision)
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
status = prot.ProvisionStatus(status="NotReady",
subStatus="ProvisioningFailed",
description= text(e))
try:
protocol.report_provision_status(status)
except prot.ProtocolError as pe:
add_event(name="WALA", is_success=False, message=text(pe),
op=WALAEventOperation.Provision)
self.report_not_ready(protocol, "ProvisioningFailed", text(e))
self.report_event(text(e))
return
self.report_ready(protocol, thumbprint)
self.report_event("Provision succeed", is_success=True)
add_event(name="WALA", is_success=False, message=text(e),
op=WALAEventOperation.Provision)
def wait_for_ovfenv(self, max_retry=60):
"""
Wait for cloud-init to copy ovf-env.xml file from provision ISO
"""
ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), ovf.OVF_FILE_NAME)
for retry in range(0, max_retry):
if os.path.isfile(ovf_file_path):
return
if retry < max_retry - 1:
logger.info("Wait for cloud-init to copy ovf-env.xml")
time.sleep(5)
raise ProvisionError("ovf-env.xml is not copied")
def wait_for_ssh_host_key(self, max_retry=60):
"""
Wait for cloud-init to generate ssh host key
"""
kepair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa")
path = '/etc/ssh/ssh_host_{0}_key'.format(kepair_type)
for retry in range(0, max_retry):
+4 -3
View File
@@ -27,6 +27,7 @@ import platform
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.protocol as prot
from azurelinuxagent.protocol.factory import PROT_FACTORY
from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \
DISTRO_CODE_NAME, AGENT_VERSION
from azurelinuxagent.utils.osutil import OSUTIL
@@ -67,7 +68,7 @@ class EventMonitor(object):
self.sysinfo.append(prot.TelemetryEventParam("Processors",
OSUTIL.get_processor_cores()))
try:
protocol = prot.FACTORY.get_default_protocol()
protocol = PROT_FACTORY.get_protocol()
vminfo = protocol.get_vminfo()
self.sysinfo.append(prot.TelemetryEventParam("VMName",
vminfo.vmName))
@@ -109,7 +110,7 @@ class EventMonitor(object):
data = json.loads(data_str)
except ValueError as e:
logger.verb(data_str)
logger.error("Failed to decode json event file: {0}", e)
logger.verb("Failed to decode json event file: {0}", e)
continue
event = prot.TelemetryEvent()
@@ -120,7 +121,7 @@ class EventMonitor(object):
return
try:
protocol = prot.FACTORY.get_default_protocol()
protocol = PROT_FACTORY.get_protocol()
protocol.report_event(event_list)
except prot.ProtocolError as e:
logger.error("{0}", e)
-3
View File
@@ -18,6 +18,3 @@
#
from azurelinuxagent.protocol.common import *
from azurelinuxagent.protocol.protocolFactory import FACTORY, \
detect_default_protocol
@@ -19,53 +19,27 @@ import os
import socket
import array
import time
import threading
import azurelinuxagent.logger as logger
from azurelinuxagent.utils.osutil import OSUTIL
from azurelinuxagent.exception import AgentNetworkError
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
from azurelinuxagent.utils.textutil import *
from azurelinuxagent.protocol.common import ProtocolError
WIRE_SERVER_ADDR_FILE_NAME="WireServer"
class DhcpHandler(object):
def __init__(self):
self.endpoint = None
self.gateway = None
self.routes = None
def wait_for_network(self):
ipv4 = OSUTIL.get_ip4_addr()
while ipv4 == '' or ipv4 == '0.0.0.0':
logger.info("Waiting for network.")
time.sleep(10)
OSUTIL.start_network()
ipv4 = OSUTIL.get_ip4_addr()
def probe(self):
logger.info("Send dhcp request")
self.wait_for_network()
mac_addr = OSUTIL.get_mac_addr()
req = build_dhcp_request(mac_addr)
resp = send_dhcp_request(req)
if resp is None:
logger.warn("Failed to detect wire server.")
return
DHCP_FILE_NAME = "DHCP"
class DhcpResponse(object):
def __init__(self, resp):
endpoint, gateway, routes = parse_dhcp_resp(resp)
self.endpoint = endpoint
logger.info("Wire server endpoint:{0}", endpoint)
logger.info("Gateway:{0}", gateway)
logger.info("Routes:{0}", routes)
if endpoint is not None:
path = os.path.join(OSUTIL.get_lib_dir(), WIRE_SERVER_ADDR_FILE_NAME)
fileutil.write_file(path, endpoint)
logger.verb("Wire server endpoint:{0}", endpoint)
logger.verb("Gateway:{0}", gateway)
logger.verb("Routes:{0}", routes)
self.gateway = gateway
self.routes = routes
self.conf_routes()
def get_endpoint(self):
return self.endpoint
def conf_routes(self):
logger.info("Configure routes")
#Add default gateway
@@ -75,6 +49,52 @@ class DhcpHandler(object):
for route in self.routes:
OSUTIL.route_add(route[0], route[1], route[2])
def _load_dhcp_resp():
dhcp_file_path = os.path.join(OSUTIL.get_lib_dir(), DHCP_FILE_NAME)
resp = fileutil.read_file(dhcp_file_path, asbin=True)
return DhcpResponse(resp)
def _fetch_dhcp_resp():
logger.info("Send dhcp request")
mac_addr = OSUTIL.get_mac_addr()
req = build_dhcp_request(mac_addr)
resp = send_dhcp_request(req)
if resp is None:
raise ProtocolError("Failed to receive dhcp response.")
dhcp_file_path = os.path.join(OSUTIL.get_lib_dir(), DHCP_FILE_NAME)
try:
fileutil.write_file(dhcp_file_path, resp, asbin=True)
except IOError as e:
logger.warn("Failed to save dhcp response: {0}", e)
return DhcpResponse(resp)
class DhcpClient(object):
def __init__(self):
self._resp = None
self._lock = threading.Lock()
def get_dhcp_resp(self):
self._lock.acquire()
try:
if self._resp is None:
try:
self._resp = _load_dhcp_resp()
except IOError:
self._resp = _fetch_dhcp_resp()
return self._resp
finally:
self._lock.release()
def fetch_dhcp_resp(self):
self._lock.acquire()
try:
self._resp = _fetch_dhcp_resp()
return self._resp
finally:
self._lock.release()
DHCPCLIENT = DhcpClient()
def validate_dhcp_resp(request, response):
bytes_recv = len(response)
if bytes_recv < 0xF6:
@@ -92,28 +112,25 @@ def validate_dhcp_resp(request, response):
logger.verb("Cookie not match:\nsend={0},\nreceive={1}",
hex_dump3(request, 0xEC, 4),
hex_dump3(response, 0xEC, 4))
raise AgentNetworkError("Cookie in dhcp respones "
"doesn't match the request")
raise ProtocolError("Cookie in dhcp respones doesn't match the request")
if not compare_bytes(request, response, 4, 4):
logger.verb("TransactionID not match:\nsend={0},\nreceive={1}",
hex_dump3(request, 4, 4),
hex_dump3(response, 4, 4))
raise AgentNetworkError("TransactionID in dhcp respones "
"doesn't match the request")
raise ProtocolError("TransactionID in dhcp respones "
"doesn't match the request")
if not compare_bytes(request, response, 0x1C, 6):
logger.verb("Mac Address not match:\nsend={0},\nreceive={1}",
hex_dump3(request, 0x1C, 6),
hex_dump3(response, 0x1C, 6))
raise AgentNetworkError("Mac Addr in dhcp respones "
"doesn't match the request")
raise ProtocolError("Mac Addr in dhcp respones "
"doesn't match the request")
def parse_route(response, option, i, length, bytes_recv):
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
logger.verb("Routes at offset: {0} with length:{1}",
hex(i),
hex(length))
logger.verb("Routes at offset: {0} with length:{1}", hex(i), hex(length))
routes = []
if length < 5:
logger.error("Data too small for option:{0}", option)
@@ -169,9 +186,7 @@ def parse_dhcp_resp(response):
if (i + 1) < bytes_recv:
length = str_to_ord(response[i + 1])
logger.verb("DHCP option {0} at offset:{1} with length:{2}",
hex(option),
hex(i),
hex(length))
hex(option), hex(i), hex(length))
if option == 255:
logger.verb("DHCP packet ended at offset:{0}", hex(i))
break
@@ -179,19 +194,14 @@ def parse_dhcp_resp(response):
routes = parse_route(response, option, i, length, bytes_recv)
elif option == 3:
gateway = parse_ip_addr(response, option, i, length, bytes_recv)
logger.verb("Default gateway:{0}, at {1}",
gateway,
hex(i))
logger.verb("Default gateway:{0}, at {1}", gateway, hex(i))
elif option == 245:
endpoint = parse_ip_addr(response, option, i, length, bytes_recv)
logger.verb("Azure wire protocol endpoint:{0}, at {1}",
gateway,
hex(i))
logger.verb("Azure wire protocol endpoint:{0}, at {1}", gateway,
hex(i))
else:
logger.verb("Skipping DHCP option:{0} at {1} with length {2}",
hex(option),
hex(i),
hex(length))
hex(option), hex(i), hex(length))
i += length + 2
return endpoint, gateway, routes
@@ -237,7 +247,7 @@ def send_dhcp_request(request):
response = socket_send(request)
validate_dhcp_resp(request, response)
return response
except AgentNetworkError as e:
except ProtocolError as e:
logger.warn("Failed to send DHCP request: {0}", e)
time.sleep(duration)
return None
@@ -257,7 +267,7 @@ def socket_send(request):
response = sock.recv(1024)
return response
except IOError as e:
raise AgentNetworkError("{0}".format(e))
raise ProtocolError("{0}".format(e))
finally:
if sock is not None:
sock.close()
+124
View File
@@ -0,0 +1,124 @@
# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Requires Python 2.4+ and Openssl 1.0+
#
import os
import traceback
import time
import threading
import azurelinuxagent.logger as logger
from azurelinuxagent.exception import *
from azurelinuxagent.future import text
import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.utils.osutil import OSUTIL
from azurelinuxagent.protocol.common import *
from azurelinuxagent.protocol.v1 import WireProtocol
from azurelinuxagent.protocol.v2 import MetadataProtocol
from azurelinuxagent.protocol.ovfenv import TAG_FILE_NAME
PROTOCOL_FILE_NAME = "Protocol"
MAX_RETRY = 60
def _detect_protocol(protocols=[WireProtocol, MetadataProtocol]):
protocol_file_path = os.path.join(OSUTIL.get_lib_dir(), PROTOCOL_FILE_NAME)
if os.path.isfile(protocol_file_path):
os.remove(protocol_file_path)
for retry in range(0, MAX_RETRY):
for protocol_cls in protocols:
try:
logger.info("Detecting protocol: {0}", protocol_cls.__name__)
protocol = protocol_cls()
protocol.initialize()
logger.info("Found protocol: {0}", protocol_cls.__name__)
fileutil.write_file(protocol_file_path, protocol_cls.__name__)
return protocol
except ProtocolError as e:
logger.info("Protocol endpoint not found: {0}, {1}",
protocol_cls.__name__, e)
if retry < MAX_RETRY -1:
logger.info("Retry detect protocols: retry={0}", retry)
time.sleep(10)
raise ProtocolNotFound("No protocol found.")
def _get_protocol():
protocol_file_path = os.path.join(OSUTIL.get_lib_dir(),
PROTOCOL_FILE_NAME)
if not os.path.isfile(protocol_file_path):
raise ProtocolError("No protocl found")
protocol_name = fileutil.read_file(protocol_file_path)
if protocol_name == WireProtocol.__name__:
return WireProtocol()
else:
return MetadataProtocol()
class ProtocolFactory(object):
def __init__(self):
self.protocol = None
self.lock = threading.Lock()
def detect_protocol(self):
logger.info("Detect protocol endpoints")
self.lock.acquire()
try:
if self.protocol is None:
self.protocol = _detect_protocol()
return self.protocol
finally:
self.lock.release()
def detect_protocol_by_file(self):
logger.info("Detect protocol by file")
self.lock.acquire()
try:
tag_file_path = os.path.join(OSUTIL.get_lib_dir(), TAG_FILE_NAME)
if self.protocol is None:
if os.path.isfile(tag_file_path):
protocol = _detect_protocol(protocols=[MetadataProtocol])
else:
protocol = _detect_protocol(protocols=[WireProtocol])
self.protocol = protocol
return self.protocol
finally:
self.lock.release()
def get_protocol(self):
"""
Get protocol detected
"""
self.lock.acquire()
try:
if self.protocol is None:
self.protocol = _get_protocol()
return self.protocol
finally:
self.lock.release()
return self.protocol
def wait_for_network(self):
"""
Wait for network stack to be initialized
"""
ipv4 = OSUTIL.get_ip4_addr()
while ipv4 == '' or ipv4 == '0.0.0.0':
logger.info("Waiting for network.")
time.sleep(10)
OSUTIL.start_network()
ipv4 = OSUTIL.get_ip4_addr()
PROT_FACTORY = ProtocolFactory()
+16 -2
View File
@@ -17,19 +17,24 @@
# Requires Python 2.4+ and Openssl 1.0+
#
"""
Copy and parse ovf-env.xml from provisiong ISO and local cache
Copy and parse ovf-env.xml from provisioning ISO and local cache
"""
import os
import re
import shutil
import xml.dom.minidom as minidom
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext
from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
from azurelinuxagent.protocol import ProtocolError
from azurelinuxagent.protocol.common import ProtocolError
OVF_FILE_NAME = "ovf-env.xml"
#Tag file to indicate usage of metadata protocol
TAG_FILE_NAME = "useMetadataEndpoint.tag"
OVF_VERSION = "1.0"
OVF_NAME_SPACE = "http://schemas.dmtf.org/ovf/environment/1"
WA_NAME_SPACE = "http://schemas.microsoft.com/windowsazure"
@@ -52,12 +57,21 @@ def copy_ovf_env():
"""
try:
OSUTIL.mount_dvd()
ovf_file_path_on_dvd = OSUTIL.get_ovf_env_file_path_on_dvd()
ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True)
ovfenv = OvfEnv(ovfxml)
ovfxml = re.sub("<UserPassword>.*?<", "<UserPassword>*<", ovfxml)
ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), OVF_FILE_NAME)
fileutil.write_file(ovf_file_path, ovfxml)
tag_file_path_on_dvd = os.path.join(OSUTIL.get_dvd_mount_point(),
TAG_FILE_NAME)
if os.path.isfile(tag_file_path_on_dvd):
logger.info("Found {0} in provisioning ISO", TAG_FILE_NAME)
tag_file_path = os.path.join(OSUTIL.get_lib_dir(), TAG_FILE_NAME)
shutil.copyfile(tag_file_path_on_dvd, tag_file_path)
OSUTIL.umount_dvd()
OSUTIL.eject_dvd()
except IOError as e:
-113
View File
@@ -1,113 +0,0 @@
# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Requires Python 2.4+ and Openssl 1.0+
#
import os
import traceback
import threading
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.utils.osutil import OSUTIL
from azurelinuxagent.protocol.common import *
from azurelinuxagent.protocol.v1 import WireProtocol
from azurelinuxagent.protocol.v2 import MetadataProtocol
WIRE_SERVER_ADDR_FILE_NAME = "WireServer"
def get_wire_protocol_endpoint():
path = os.path.join(OSUTIL.get_lib_dir(), WIRE_SERVER_ADDR_FILE_NAME)
try:
endpoint = fileutil.read_file(path)
except IOError as e:
raise ProtocolNotFound("Wire server endpoint not found: {0}".format(e))
if endpoint is None:
raise ProtocolNotFound("Wire server endpoint is None")
return endpoint
def detect_wire_protocol():
endpoint = get_wire_protocol_endpoint()
protocol = WireProtocol(endpoint)
protocol.initialize()
logger.info("Protocol V1 found.")
return protocol
def detect_metadata_protocol():
protocol = MetadataProtocol()
protocol.initialize()
logger.info("Protocol V2 found.")
return protocol
def detect_available_protocols(prob_funcs=[detect_wire_protocol,
detect_metadata_protocol]):
available_protocols = []
for probe_func in prob_funcs:
try:
protocol = probe_func()
available_protocols.append(protocol)
except ProtocolNotFound as e:
logger.info(text(e))
return available_protocols
def detect_default_protocol():
logger.info("Detect default protocol.")
available_protocols = detect_available_protocols()
return choose_default_protocol(available_protocols)
def choose_default_protocol(protocols):
if len(protocols) > 0:
return protocols[0]
else:
raise ProtocolNotFound("No available protocol detected.")
def get_wire_protocol():
endpoint = get_wire_protocol_endpoint()
return WireProtocol(endpoint)
def get_metadata_protocol():
return MetadataProtocol()
def get_available_protocols(getters=[get_wire_protocol, get_metadata_protocol]):
available_protocols = []
for getter in getters:
try:
protocol = getter()
available_protocols.append(protocol)
except ProtocolNotFound as e:
logger.info(text(e))
return available_protocols
class ProtocolFactory(object):
def __init__(self):
self._protocol = None
self._lock = threading.Lock()
def get_default_protocol(self):
if self._protocol is None:
self._lock.acquire()
if self._protocol is None:
available_protocols = get_available_protocols()
self._protocol = choose_default_protocol(available_protocols)
self._lock.release()
return self._protocol
FACTORY = ProtocolFactory()
+10 -3
View File
@@ -33,6 +33,7 @@ from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
from azurelinuxagent.protocol.common import *
import azurelinuxagent.protocol.dhcp as dhcp
VERSION_INFO_URI = "http://{0}/?comp=versions"
GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate"
@@ -54,19 +55,23 @@ TRANSPORT_CERT_FILE_NAME = "TransportCert.pem"
TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
PROTOCOL_VERSION = "2012-11-30"
ENDPOINT_FINE_NAME = "WireServer"
class WireProtocolResourceGone(ProtocolError):
pass
class WireProtocol(Protocol):
def __init__(self, endpoint):
self.client = WireClient(endpoint)
def __init__(self):
dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp()
self.client = WireClient(dhcp_resp.endpoint)
def initialize(self):
dhcp_resp = dhcp.DHCPCLIENT.fetch_dhcp_resp()
self.client = WireClient(dhcp_resp.endpoint)
self.client.check_wire_protocol_version()
OSUTIL.gen_transport_cert(TRANSPORT_PRV_FILE_NAME,
TRANSPORT_CERT_FILE_NAME)
self.client.check_wire_protocol_version()
self.client.update_goal_state(forced=True)
def get_vminfo(self):
@@ -462,6 +467,8 @@ def event_to_v1(event):
class WireClient(object):
def __init__(self, endpoint):
if endpoint is None:
raise ProtocolError("WireProtocl endpoint is None")
self.endpoint = endpoint
self.goal_state = None
self.updated = None
+2 -12
View File
@@ -108,6 +108,7 @@ class MetadataProtocol(Protocol):
return textutil.get_bytes_from_pem(content)
def initialize(self):
self.get_vminfo()
trans_prv_file = os.path.join(OSUTIL.get_lib_dir(),
TRANSPORT_PRV_FILE_NAME)
trans_crt_file = os.path.join(OSUTIL.get_lib_dir(),
@@ -122,18 +123,7 @@ class MetadataProtocol(Protocol):
"{0}.crt".format(thumbprint))
shutil.copyfile(trans_prv_file, prv_file)
shutil.copyfile(trans_crt_file, crt_file)
#TODO remote workarround for azure stack test
for retry in range(0, MAX_PING):
try:
self.get_vminfo()
return
except ProtocolError as e:
logger.warn("Metadata server is not ready, retry = {0}", retry)
if retry < MAX_PING - 1:
time.sleep(RETRY_PING_INTERVAL)
raise ProtocolNotFound("Metadata server endpoint is not reachable")
def get_vminfo(self):
vminfo = VMInfo()
data = self._get_data(self.identity_uri)
+2 -2
View File
@@ -36,7 +36,7 @@ def MockSetup(self, deluser):
class TestDeprovisionHandler(unittest.TestCase):
def test_setup(self):
handler = deprovision_handler.DeprovisionHandler()
handler = deprovision_handler.DeprovisionHandler(None)
warnings, actions = handler.setup(False)
self.assertNotEquals(None, warnings)
self.assertNotEquals(0, len(warnings))
@@ -47,7 +47,7 @@ class TestDeprovisionHandler(unittest.TestCase):
@mock(deprovision_handler.DeprovisionHandler, 'setup', MockSetup)
def test_deprovision(self):
handler = deprovision_handler.DeprovisionHandler()
handler = deprovision_handler.DeprovisionHandler(None)
handler.deprovision(force=True)
if __name__ == '__main__':
+15 -17
View File
@@ -25,7 +25,7 @@ import unittest
import os
import json
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.distro.default.dhcp as dhcp_handler
import azurelinuxagent.protocol.dhcp as dhcp
SampleDhcpResponse = None
with open(os.path.join(env.test_root, "dhcp"), 'rb') as F:
@@ -39,30 +39,28 @@ mock_send_dhcp_failed = MockFunc(retval=None)
class TestdhcpHandler(unittest.TestCase):
def test_build_dhcp_req(self):
req = dhcp_handler.build_dhcp_request(mock_get_mac_addr())
req = dhcp.build_dhcp_request(mock_get_mac_addr())
self.assertNotEquals(None, req)
@mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id)
@mock(dhcp_handler, "socket_send", mock_socket_send)
@mock(dhcp, "gen_trans_id", mock_gen_trans_id)
@mock(dhcp, "socket_send", mock_socket_send)
def test_send_dhcp_req(self):
req = dhcp_handler.build_dhcp_request(mock_get_mac_addr())
resp = dhcp_handler.send_dhcp_request(req)
req = dhcp.build_dhcp_request(mock_get_mac_addr())
resp = dhcp.send_dhcp_request(req)
self.assertNotEquals(None, resp)
@mock(dhcp_handler, "send_dhcp_request", mock_send_dhcp_failed)
@mock(dhcp, "send_dhcp_request", mock_send_dhcp_failed)
def test_send_dhcp_failed(self):
dhcp = dhcp_handler.DhcpHandler()
dhcp.probe()
dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp()
@mock(dhcp_handler, "socket_send", mock_socket_send)
@mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id)
@mock(dhcp_handler.OSUTIL, "get_mac_addr", mock_get_mac_addr)
@mock(dhcp_handler.fileutil, "write_file", MockFunc())
@mock(dhcp, "socket_send", mock_socket_send)
@mock(dhcp, "gen_trans_id", mock_gen_trans_id)
@mock(dhcp.OSUTIL, "get_mac_addr", mock_get_mac_addr)
@mock(dhcp.fileutil, "write_file", MockFunc())
def test_handle_dhcp(self):
dh = dhcp_handler.DhcpHandler()
dh.probe()
self.assertEquals("10.62.144.1", dh.gateway)
self.assertEquals("10.62.144.140", dh.endpoint)
dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp()
self.assertEquals("10.62.144.1", dhcp_resp.gateway)
self.assertEquals("10.62.144.140", dhcp_resp.endpoint)
if __name__ == '__main__':
unittest.main()
-1
View File
@@ -31,7 +31,6 @@ class TestDistroLoader(unittest.TestCase):
self.assertNotEquals(None, HANDLERS.init_handler)
self.assertNotEquals(None, HANDLERS.main_handler)
self.assertNotEquals(None, HANDLERS.scvmm_handler)
self.assertNotEquals(None, HANDLERS.dhcp_handler)
self.assertNotEquals(None, HANDLERS.env_handler)
self.assertNotEquals(None, HANDLERS.provision_handler)
self.assertNotEquals(None, HANDLERS.resource_disk_handler)
+9 -3
View File
@@ -22,14 +22,18 @@ import tests.env
from tests.tools import *
import unittest
import time
import azurelinuxagent.protocol.dhcp as dhcp
from azurelinuxagent.future import text
from azurelinuxagent.utils.osutil import OSUTIL
from azurelinuxagent.distro.default.env import EnvMonitor
class MockDhcpHandler(object):
class MockDhcpResponse(object):
def conf_routes(self):
pass
def mock_get_dhcp_resp(self):
return MockDhcpResponse()
def mock_get_dhcp_pid():
return "1234"
@@ -39,13 +43,15 @@ def mock_dhcp_pid_change():
class TestEnvMonitor(unittest.TestCase):
@mock(OSUTIL, 'get_dhcp_pid', mock_get_dhcp_pid)
@mock(dhcp.DHCPCLIENT, 'get_dhcp_resp', mock_get_dhcp_resp)
def test_dhcp_pid_not_change(self):
monitor = EnvMonitor(MockDhcpHandler())
monitor = EnvMonitor()
monitor.handle_dhclient_restart()
@mock(OSUTIL, 'get_dhcp_pid', mock_dhcp_pid_change)
@mock(dhcp.DHCPCLIENT, 'get_dhcp_resp', mock_get_dhcp_resp)
def test_dhcp_pid_change(self):
monitor = EnvMonitor(MockDhcpHandler())
monitor = EnvMonitor()
monitor.handle_dhclient_restart()
if __name__ == '__main__':
+2 -2
View File
@@ -27,6 +27,7 @@ import shutil
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.event as evt
import azurelinuxagent.protocol as prot
from azurelinuxagent.protocol.factory import PROT_FACTORY
class MockProtocol(object):
def get_vminfo(self):
@@ -42,8 +43,7 @@ class TestEvent(unittest.TestCase):
self.assertNotEquals(0, len(eventsFile))
shutil.rmtree("/tmp/events")
@mock(evt.prot.FACTORY, 'get_default_protocol',
MockFunc(retval=MockProtocol()))
@mock(PROT_FACTORY, 'get_protocol', MockFunc(retval=MockProtocol()))
def test_init_sys_info(self):
monitor = evt.EventMonitor()
monitor.init_sysinfo()
+1 -1
View File
@@ -27,7 +27,7 @@ import json
import azurelinuxagent.logger as logger
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.protocol as prot
import azurelinuxagent.protocol.common as prot
import azurelinuxagent.distro.default.extension as ext
ext_sample_json = {
-37
View File
@@ -1,37 +0,0 @@
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Requires Python 2.4+ and Openssl 1.0+
#
# Implements parts of RFC 2131, 1541, 1497 and
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
import tests.env
from tests.tools import *
import uuid
import unittest
import os
import azurelinuxagent.protocol as protocol
import azurelinuxagent.protocol.protocolFactory as protocolFactory
class TestWireProtocolEndpoint(unittest.TestCase):
def test_get_available_protocols(self):
mockGetV1 = MockFunc(retval="Mock protocol")
protocols = protocolFactory.get_available_protocols([mockGetV1])
self.assertNotEquals(None, protocols)
self.assertNotEquals(0, len(protocols))
if __name__ == '__main__':
unittest.main()
+3 -3
View File
@@ -44,19 +44,19 @@ class TestResourceDisk(unittest.TestCase):
@mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, gpt_output_sample)))
@mock(rdh.shellutil, 'run', MockFunc(retval=0))
def test_mountGPT(self):
handler = rdh.ResourceDiskHandler()
handler = rdh.ResourceDiskHandler(None)
handler.mount_resource_disk('/tmp/foo', 'ext4')
@mock(rdh.OSUTIL, 'device_for_ide_port', MockFunc(retval='foo'))
@mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, "")))
@mock(rdh.shellutil, 'run', MockFunc(retval=0))
def test_mountMBR(self):
handler = rdh.ResourceDiskHandler()
handler = rdh.ResourceDiskHandler(None)
handler.mount_resource_disk('/tmp/foo', 'ext4')
@mock(rdh.shellutil, 'run', MockFunc(retval=0))
def test_createSwapSpace(self):
handler = rdh.ResourceDiskHandler()
handler = rdh.ResourceDiskHandler(None)
handler.create_swap_space('/tmp/foo', 512)
if __name__ == '__main__':