diff --git a/cloudinit/config/cc_setup_azure_networking.py b/cloudinit/config/cc_setup_azure_networking.py new file mode 100644 index 0000000..0e96bbd --- /dev/null +++ b/cloudinit/config/cc_setup_azure_networking.py @@ -0,0 +1,52 @@ +# Copyright (C) Microsoft Corp. +# +# Author: Tamilmani Manoharan +# +# This file is part of cloud-init. See LICENSE file for license information. + +""" +Setup Azure Networking +------------------ +**Summary:** sets up networking required only for azure virtual machines. + +This module handles any specific networking requirements for azure virtual machines (VMs). +In the present implementation, it handles moving a virtual machine from one azure virtual +network to another. + +**Internal name:** ``cc_setup_azure_networking`` + +**Module frequency:** per instance + +**Supported distros:** all +""" + +from cloudinit.settings import PER_ALWAYS +import time +import os +import socket +import struct +import glob +from subprocess import Popen +from cloudinit import importer +from cloudinit import config +from cloudinit import type_utils + +frequency = PER_ALWAYS + +def IsAzure(): + azure_config_exists = False + for n in glob.glob("/etc/cloud/cloud.cfg.d/*azure.cfg"): + if os.path.isfile(n): + azure_config_exists=True + return azure_config_exists + +def handle(name, _cfg, _cloud, log, _args): + if IsAzure(): + configs_dir = os.path.dirname(os.path.abspath(__file__)) + log.debug("config dir %s", configs_dir) + proc = Popen(["python", configs_dir + "/../sources/helpers/azuredhcpagent.py"]) + log.debug("Created netagent process") + + + + \ No newline at end of file diff --git a/cloudinit/sources/helpers/azuredhcpagent.py b/cloudinit/sources/helpers/azuredhcpagent.py new file mode 100644 index 0000000..349b668 --- /dev/null +++ b/cloudinit/sources/helpers/azuredhcpagent.py @@ -0,0 +1,125 @@ +# Author: Tamilmani Manoharan +# +# This file is part of cloud-init. See LICENSE file for license information. + +import sys +import logging +import socket +import os +import struct +import subprocess +from logging.handlers import RotatingFileHandler + +RTMGRP_LINK = 1 +NLMSG_NOOP = 1 +NLMSG_ERROR = 2 +RTM_NEWLINK = 16 +RTM_DELLINK = 17 +IFLA_IFNAME = 3 + +def GetLogger(): + log = logging.getLogger("azuredhcpagent") + log.setLevel(logging.DEBUG) + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)s - %(message)s') + + handler = RotatingFileHandler("/var/log/azuredhcpagent.log", maxBytes=10485760, + backupCount=5) + + handler.setFormatter(formatter) + log.addHandler(handler) + return log + +def main(): + + log = GetLogger() + # Create the netlink socket and bind to RTMGRP_LINK, + s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE) + s.bind((os.getpid(), RTMGRP_LINK)) + + while True: + + data = s.recv(65535) + msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", data[:16]) + + if msg_type == NLMSG_NOOP: + log.debug("noop") + continue + elif msg_type == NLMSG_ERROR: + log.debug("error") + break + + # We fundamentally only care about NEWLINK messages in this version. + if msg_type != RTM_NEWLINK: + continue + + data = data[16:] + + family, _, if_type, index, flags, change = struct.unpack("=BBHiII", data[:16]) + + remaining = msg_len - 32 + data = data[16:] + + while remaining: + rta_len, rta_type = struct.unpack("=HH", data[:4]) + + # This check comes from RTA_OK, and terminates a string of routing + # attributes. + if rta_len < 4: + break + + rta_data = data[4:rta_len] + + increment = (rta_len + 4 - 1) & ~(4 - 1) + data = data[increment:] + remaining -= increment + + # The link is up! + if rta_type == IFLA_IFNAME: + log.debug("New link %s", rta_data) + ifname = str(rta_data).strip('\0') + + operfilename = "/sys/class/net/" + ifname + "/operstate" + operfilename = operfilename.strip("\0") + carrierfilename = "/sys/class/net/" + ifname + "/carrier" + carrierfilename = carrierfilename.strip("\0") + + log.debug("operfilename %s", operfilename) + + carrier="" + operstate="" + + try: + file = open(operfilename, "r") + operstate = file.readline() + file.close() + except Exception as e: + log.error("exception reading operstate %s", str(e)) + + operstate = operstate.rstrip() + log.debug("operstate %s", operstate) + + try: + file = open(carrierfilename, "r") + carrier = file.readline() + file.close() + + except IOError as io: + if io.errno != 22: + log.error("IO error reading carrier %s errno %d", str(io), io.errno) + + except Exception as e: + log.error("exception reading carrier %s", str(e)) + + + carrier = carrier.rstrip() + log.debug("carrier %s", carrier) + + if operstate == "up" or carrier == "1": + log.debug("trigger dhcp") + # assuming dhclient exists + return_code = subprocess.call("dhclient " + ifname, shell=True) + log.debug("dhclient return status %d", return_code) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl index 32de9c9..f61f2f3 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -125,7 +125,8 @@ cloud_final_modules: - phone-home - final-message - power-state-change - + - setup-azure-networking + # System and/or distro specific settings # (not accessible to handlers/transforms) system_info: