From 76e17be5dec7b143c002c5226f95d2c35e640f28 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Mon, 15 Jan 2018 13:12:23 -0800 Subject: [PATCH 1/4] cloudinit redhcp changes --- cloudinit/config/cc_dhcpagent.py | 74 ++++++++++++ cloudinit/sources/helpers/azuredhcpagent.py | 120 ++++++++++++++++++++ config/cloud.cfg.tmpl | 1 + 3 files changed, 195 insertions(+) create mode 100644 cloudinit/config/cc_dhcpagent.py create mode 100644 cloudinit/sources/helpers/azuredhcpagent.py diff --git a/cloudinit/config/cc_dhcpagent.py b/cloudinit/config/cc_dhcpagent.py new file mode 100644 index 0000000..f86a603 --- /dev/null +++ b/cloudinit/config/cc_dhcpagent.py @@ -0,0 +1,74 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +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 +# Modules are expected to have the following attributes. +# 1. A required 'handle' method which takes the following params. +# a) The name will not be this files name, but instead +# the name specified in configuration (which is the name +# which will be used to find this module). +# b) A configuration object that is the result of the merging +# of cloud configs configuration with legacy configuration +# as well as any datasource provided configuration +# c) A cloud object that can be used to access various +# datasource and paths for the given distro and data provided +# by the various datasource instance types. +# d) A argument list that may or may not be empty to this module. +# Typically those are from module configuration where the module +# is defined with some extra configuration that will eventually +# be translated from yaml into arguments to this module. +# 2. A optional 'frequency' that defines how often this module should be ran. +# Typically one of PER_INSTANCE, PER_ALWAYS, PER_ONCE. If not +# provided PER_INSTANCE will be assumed. +# See settings.py for these constants. +# 3. A optional 'distros' array/set/tuple that defines the known distros +# this module will work with (if not all of them). This is used to write +# a warning out if a module is being ran on a untested distribution for +# informational purposes. If non existent all distros are assumed and +# no warning occurs. + +frequency = PER_ALWAYS + +def IsAzureCloud(): + flag = False + for n in glob.glob("/etc/cloud/cloud.cfg.d/*azure.cfg"): + if os.path.isfile(n): + flag=True + return flag + +def handle(name, _cfg, _cloud, log, _args): + if IsAzureCloud(): + 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..0cba27b --- /dev/null +++ b/cloudinit/sources/helpers/azuredhcpagent.py @@ -0,0 +1,120 @@ +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("dhcpagent") + log.setLevel(logging.DEBUG) + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)s - %(message)s') + + handler = RotatingFileHandler("/var/log/dhcpagent.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 + + # Hoorah, a 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") + 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..a45436b 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -113,6 +113,7 @@ cloud_final_modules: - chef - salt-minion - mcollective + - dhcpagent {% endif %} - rightscale_userdata - scripts-vendor From 48c9775d4ad2cb50e867d539147ec87c01f90ecf Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Mon, 15 Jan 2018 13:16:25 -0800 Subject: [PATCH 2/4] modiifed dhcpagnet module started for all linux variants --- config/cloud.cfg.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl index a45436b..0f81b2d 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -113,7 +113,6 @@ cloud_final_modules: - chef - salt-minion - mcollective - - dhcpagent {% endif %} - rightscale_userdata - scripts-vendor @@ -126,7 +125,8 @@ cloud_final_modules: - phone-home - final-message - power-state-change - + - dhcpagent + # System and/or distro specific settings # (not accessible to handlers/transforms) system_info: From 2cdbc749c3356b2a4812535ffccac7e211718ac1 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 18 Jan 2018 14:53:00 -0800 Subject: [PATCH 3/4] Fix headers --- cloudinit/config/cc_dhcpagent.py | 74 ------------------- cloudinit/config/cc_setup_azure_networking.py | 52 +++++++++++++ cloudinit/sources/helpers/azuredhcpagent.py | 15 ++-- config/cloud.cfg.tmpl | 2 +- 4 files changed, 63 insertions(+), 80 deletions(-) delete mode 100644 cloudinit/config/cc_dhcpagent.py create mode 100644 cloudinit/config/cc_setup_azure_networking.py diff --git a/cloudinit/config/cc_dhcpagent.py b/cloudinit/config/cc_dhcpagent.py deleted file mode 100644 index f86a603..0000000 --- a/cloudinit/config/cc_dhcpagent.py +++ /dev/null @@ -1,74 +0,0 @@ -# vi: ts=4 expandtab -# -# Copyright (C) 2009-2010 Canonical Ltd. -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# -# Author: Scott Moser -# Author: Juerg Haefliger -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -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 -# Modules are expected to have the following attributes. -# 1. A required 'handle' method which takes the following params. -# a) The name will not be this files name, but instead -# the name specified in configuration (which is the name -# which will be used to find this module). -# b) A configuration object that is the result of the merging -# of cloud configs configuration with legacy configuration -# as well as any datasource provided configuration -# c) A cloud object that can be used to access various -# datasource and paths for the given distro and data provided -# by the various datasource instance types. -# d) A argument list that may or may not be empty to this module. -# Typically those are from module configuration where the module -# is defined with some extra configuration that will eventually -# be translated from yaml into arguments to this module. -# 2. A optional 'frequency' that defines how often this module should be ran. -# Typically one of PER_INSTANCE, PER_ALWAYS, PER_ONCE. If not -# provided PER_INSTANCE will be assumed. -# See settings.py for these constants. -# 3. A optional 'distros' array/set/tuple that defines the known distros -# this module will work with (if not all of them). This is used to write -# a warning out if a module is being ran on a untested distribution for -# informational purposes. If non existent all distros are assumed and -# no warning occurs. - -frequency = PER_ALWAYS - -def IsAzureCloud(): - flag = False - for n in glob.glob("/etc/cloud/cloud.cfg.d/*azure.cfg"): - if os.path.isfile(n): - flag=True - return flag - -def handle(name, _cfg, _cloud, log, _args): - if IsAzureCloud(): - 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/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 index 0cba27b..349b668 100644 --- a/cloudinit/sources/helpers/azuredhcpagent.py +++ b/cloudinit/sources/helpers/azuredhcpagent.py @@ -1,3 +1,7 @@ +# Author: Tamilmani Manoharan +# +# This file is part of cloud-init. See LICENSE file for license information. + import sys import logging import socket @@ -14,11 +18,11 @@ IFLA_IFNAME = 3 def GetLogger(): - log = logging.getLogger("dhcpagent") + 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/dhcpagent.log", maxBytes=10485760, + handler = RotatingFileHandler("/var/log/azuredhcpagent.log", maxBytes=10485760, backupCount=5) handler.setFormatter(formatter) @@ -69,14 +73,14 @@ def main(): data = data[increment:] remaining -= increment - # Hoorah, a link is up! + # 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 = "/sys/class/net/" + ifname + "/operstate" operfilename = operfilename.strip("\0") - carrierfilename = "/sys/class/net/" + ifname +"/carrier" + carrierfilename = "/sys/class/net/" + ifname + "/carrier" carrierfilename = carrierfilename.strip("\0") log.debug("operfilename %s", operfilename) @@ -112,6 +116,7 @@ def main(): 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) diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl index 0f81b2d..fdac46d 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -72,6 +72,7 @@ cloud_config_modules: - emit_upstart - snap_config {% endif %} + - setup-azure-networking - ssh-import-id - locale - set-passwords @@ -125,7 +126,6 @@ cloud_final_modules: - phone-home - final-message - power-state-change - - dhcpagent # System and/or distro specific settings # (not accessible to handlers/transforms) From 46a62b537ee2a3005ecd14a318fdade01a869e89 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Thu, 18 Jan 2018 17:43:54 -0800 Subject: [PATCH 4/4] moved set-azure-networking as final module --- config/cloud.cfg.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl index fdac46d..f61f2f3 100644 --- a/config/cloud.cfg.tmpl +++ b/config/cloud.cfg.tmpl @@ -72,7 +72,6 @@ cloud_config_modules: - emit_upstart - snap_config {% endif %} - - setup-azure-networking - ssh-import-id - locale - set-passwords @@ -126,6 +125,7 @@ cloud_final_modules: - phone-home - final-message - power-state-change + - setup-azure-networking # System and/or distro specific settings # (not accessible to handlers/transforms)