Remove deprecated static network config method
Refactor all the services in such way that they support the new `get_network_details` method instead of the old `get_network_config` method for statically configuring network interfaces through the networkconfig plugin. Change-Id: Idb519935da0d3aee316ad9bc9451f74bc80b12ba
This commit is contained in:
parent
b0aa3b56b8
commit
623e76997d
@ -16,7 +16,6 @@
|
||||
import abc
|
||||
import collections
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
@ -42,6 +41,7 @@ LOG = logging.getLogger(__name__)
|
||||
NetworkDetails = collections.namedtuple(
|
||||
"NetworkDetails",
|
||||
[
|
||||
"name",
|
||||
"mac",
|
||||
"address",
|
||||
"netmask",
|
||||
@ -98,7 +98,7 @@ class BaseMetadataService(object):
|
||||
pass
|
||||
|
||||
def get_content(self, name):
|
||||
pass
|
||||
"""Get raw content within a service."""
|
||||
|
||||
def get_user_data(self):
|
||||
pass
|
||||
@ -109,11 +109,6 @@ class BaseMetadataService(object):
|
||||
def get_public_keys(self):
|
||||
pass
|
||||
|
||||
def get_network_config(self):
|
||||
"""Deprecated, use `get_network_details` instead."""
|
||||
warnings.warn("deprecated method, use `get_network_details`",
|
||||
DeprecationWarning)
|
||||
|
||||
def get_network_details(self):
|
||||
"""Return a list of `NetworkDetails` objects.
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import json
|
||||
import posixpath
|
||||
|
||||
@ -19,9 +20,11 @@ from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.utils import debiface
|
||||
from cloudbaseinit.utils import encoding
|
||||
from cloudbaseinit.utils import x509constants
|
||||
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('metadata_base_url', default='http://169.254.169.254/',
|
||||
help='The base URL where the service looks for metadata'),
|
||||
@ -63,8 +66,19 @@ class BaseOpenStackService(base.BaseMetadataService):
|
||||
if public_keys:
|
||||
return public_keys.values()
|
||||
|
||||
def get_network_config(self):
|
||||
return self._get_meta_data().get('network_config')
|
||||
def get_network_details(self):
|
||||
network_config = self._get_meta_data().get('network_config')
|
||||
if not network_config:
|
||||
return None
|
||||
key = "content_path"
|
||||
if key not in network_config:
|
||||
return None
|
||||
|
||||
content_name = network_config[key].rsplit("/", 1)[-1]
|
||||
content = self.get_content(content_name)
|
||||
content = encoding.get_as_string(content)
|
||||
|
||||
return debiface.parse(content)
|
||||
|
||||
def get_admin_password(self):
|
||||
meta_data = self._get_meta_data()
|
||||
|
@ -101,6 +101,6 @@ class EC2Service(base.BaseMetadataService):
|
||||
|
||||
return ssh_keys
|
||||
|
||||
def get_network_config(self):
|
||||
# TODO(alexpilotti): add static network support
|
||||
def get_network_details(self):
|
||||
# TODO(cpoieana): add static network config support
|
||||
pass
|
||||
|
@ -31,6 +31,8 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
CONTEXT_FILE = "context.sh"
|
||||
INSTANCE_ID = "iid-dsopennebula"
|
||||
# interface name default template
|
||||
IF_FORMAT = "eth{iid}"
|
||||
|
||||
# metadata identifiers
|
||||
HOST_NAME = ["SET_HOSTNAME", "HOSTNAME"]
|
||||
@ -211,15 +213,21 @@ class OpenNebulaService(base.BaseMetadataService):
|
||||
# get existing values
|
||||
mac = self._get_cache_data(MAC, iid=iid).upper()
|
||||
address = self._get_cache_data(ADDRESS, iid=iid)
|
||||
gateway = self._get_cache_data(GATEWAY, iid=iid)
|
||||
# try to find/predict and compute the rest
|
||||
try:
|
||||
gateway = self._get_cache_data(GATEWAY, iid=iid)
|
||||
except base.NotExistingMetadataException:
|
||||
gateway = None
|
||||
try:
|
||||
netmask = self._get_cache_data(NETMASK, iid=iid)
|
||||
except base.NotExistingMetadataException:
|
||||
if not gateway:
|
||||
raise
|
||||
netmask = self._calculate_netmask(address, gateway)
|
||||
broadcast = self._compute_broadcast(address, netmask)
|
||||
# gather them as namedtuple objects
|
||||
details = base.NetworkDetails(
|
||||
IF_FORMAT.format(iid=iid),
|
||||
mac,
|
||||
address,
|
||||
netmask,
|
||||
|
@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
import os
|
||||
@ -541,19 +542,21 @@ class WindowsUtils(base.BaseOSUtils):
|
||||
"Cannot set static IP address on network adapter")
|
||||
reboot_required = (ret_val == 1)
|
||||
|
||||
LOG.debug("Setting static gateways")
|
||||
(ret_val,) = adapter_config.SetGateways([gateway], [1])
|
||||
if ret_val > 1:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Cannot set gateway on network adapter")
|
||||
reboot_required = reboot_required or ret_val == 1
|
||||
if gateway:
|
||||
LOG.debug("Setting static gateways")
|
||||
(ret_val,) = adapter_config.SetGateways([gateway], [1])
|
||||
if ret_val > 1:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Cannot set gateway on network adapter")
|
||||
reboot_required = reboot_required or ret_val == 1
|
||||
|
||||
LOG.debug("Setting static DNS servers")
|
||||
(ret_val,) = adapter_config.SetDNSServerSearchOrder(dnsnameservers)
|
||||
if ret_val > 1:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Cannot set DNS on network adapter")
|
||||
reboot_required = reboot_required or ret_val == 1
|
||||
if dnsnameservers:
|
||||
LOG.debug("Setting static DNS servers")
|
||||
(ret_val,) = adapter_config.SetDNSServerSearchOrder(dnsnameservers)
|
||||
if ret_val > 1:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Cannot set DNS on network adapter")
|
||||
reboot_required = reboot_required or ret_val == 1
|
||||
|
||||
return reboot_required
|
||||
|
||||
|
@ -13,111 +13,102 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.metadata.services import base as service_base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
from cloudbaseinit.osutils import factory as osutils_factory
|
||||
from cloudbaseinit.plugins import base as plugin_base
|
||||
from cloudbaseinit.utils import encoding
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('network_adapter', default=None, help='Network adapter to '
|
||||
'configure. If not specified, the first available ethernet '
|
||||
'adapter will be chosen.'),
|
||||
]
|
||||
# Mandatory network details are marked with True. And
|
||||
# if the key is a tuple, then at least one field must exists.
|
||||
NET_REQUIRE = {
|
||||
("name", "mac"): True,
|
||||
"address": True,
|
||||
"netmask": True,
|
||||
"broadcast": False, # currently not used
|
||||
"gateway": False,
|
||||
"dnsnameservers": False
|
||||
}
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
def _preprocess_nics(network_details, network_adapters):
|
||||
"""Check NICs and fill missing data if possible."""
|
||||
# initial checks
|
||||
if not network_adapters:
|
||||
raise exception.CloudbaseInitException(
|
||||
"no network adapters available")
|
||||
# Sort VM adapters by name (assuming that those
|
||||
# from the context are in correct order).
|
||||
network_adapters = sorted(network_adapters, key=lambda arg: arg[0])
|
||||
_network_details = [] # store here processed data
|
||||
# check and update every NetworkDetails object
|
||||
ind = 0
|
||||
total = len(network_adapters)
|
||||
for nic in network_details:
|
||||
if not isinstance(nic, service_base.NetworkDetails):
|
||||
raise exception.CloudbaseInitException(
|
||||
"invalid NetworkDetails object {!r}"
|
||||
.format(type(nic))
|
||||
)
|
||||
# check requirements
|
||||
final_status = True
|
||||
for fields, status in NET_REQUIRE.items():
|
||||
if not status:
|
||||
continue # skip 'not required' entries
|
||||
if not isinstance(fields, tuple):
|
||||
fields = (fields,)
|
||||
final_status = any([getattr(nic, field) for field in fields])
|
||||
if not final_status:
|
||||
LOG.error("Incomplete NetworkDetails object %s", nic)
|
||||
break
|
||||
if final_status:
|
||||
# Complete hardware address if missing by selecting
|
||||
# the corresponding MAC in terms of naming, then ordering.
|
||||
if not nic.mac:
|
||||
mac = None
|
||||
# by name
|
||||
macs = [adapter[1] for adapter in network_adapters
|
||||
if adapter[0] == nic.name]
|
||||
mac = macs[0] if macs else None
|
||||
# or by order
|
||||
if not mac and ind < total:
|
||||
mac = network_adapters[ind][1]
|
||||
nic = service_base.NetworkDetails(
|
||||
nic.name,
|
||||
mac,
|
||||
nic.address,
|
||||
nic.netmask,
|
||||
nic.broadcast,
|
||||
nic.gateway,
|
||||
nic.dnsnameservers
|
||||
)
|
||||
_network_details.append(nic)
|
||||
ind += 1
|
||||
return _network_details
|
||||
|
||||
|
||||
class NetworkConfigPlugin(plugin_base.BasePlugin):
|
||||
|
||||
def execute(self, service, shared_data):
|
||||
# FIXME(cpoieana): `network_config` is deprecated
|
||||
# * refactor all services by providing NetworkDetails objects *
|
||||
# Also, the old method is not supporting multiple NICs.
|
||||
|
||||
osutils = osutils_factory.get_os_utils()
|
||||
network_details = service.get_network_details()
|
||||
if not network_details:
|
||||
network_config = service.get_network_config()
|
||||
if not network_config:
|
||||
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
||||
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
# ---- BEGIN deprecated code ----
|
||||
if not network_details:
|
||||
if 'content_path' not in network_config:
|
||||
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
||||
|
||||
content_path = network_config['content_path']
|
||||
content_name = content_path.rsplit('/', 1)[-1]
|
||||
debian_network_conf = service.get_content(content_name)
|
||||
debian_network_conf = encoding.get_as_string(debian_network_conf)
|
||||
|
||||
LOG.debug('network config content:\n%s' % debian_network_conf)
|
||||
|
||||
# TODO(alexpilotti): implement a proper grammar
|
||||
m = re.search(r'iface eth0 inet static\s+'
|
||||
r'address\s+(?P<address>[^\s]+)\s+'
|
||||
r'netmask\s+(?P<netmask>[^\s]+)\s+'
|
||||
r'broadcast\s+(?P<broadcast>[^\s]+)\s+'
|
||||
r'gateway\s+(?P<gateway>[^\s]+)\s+'
|
||||
r'dns\-nameservers\s+'
|
||||
r'(?P<dnsnameservers>[^\r\n]+)\s+',
|
||||
debian_network_conf)
|
||||
if not m:
|
||||
raise exception.CloudbaseInitException(
|
||||
"network_config format not recognized")
|
||||
|
||||
mac = None
|
||||
network_adapters = osutils.get_network_adapters()
|
||||
if network_adapters:
|
||||
adapter_name = CONF.network_adapter
|
||||
if adapter_name:
|
||||
# configure with the specified one
|
||||
for network_adapter in network_adapters:
|
||||
if network_adapter[0] == adapter_name:
|
||||
mac = network_adapter[1]
|
||||
break
|
||||
else:
|
||||
# configure with the first one
|
||||
mac = network_adapters[0][1]
|
||||
network_details = [
|
||||
service_base.NetworkDetails(
|
||||
mac,
|
||||
m.group('address'),
|
||||
m.group('netmask'),
|
||||
m.group('broadcast'),
|
||||
m.group('gateway'),
|
||||
m.group('dnsnameservers').strip().split(' ')
|
||||
)
|
||||
]
|
||||
# ---- END deprecated code ----
|
||||
|
||||
# check NICs' type and save them by MAC
|
||||
# check and save NICs by MAC
|
||||
network_adapters = osutils.get_network_adapters()
|
||||
network_details = _preprocess_nics(network_details,
|
||||
network_adapters)
|
||||
macnics = {}
|
||||
for nic in network_details:
|
||||
if not isinstance(nic, service_base.NetworkDetails):
|
||||
raise exception.CloudbaseInitException(
|
||||
"invalid NetworkDetails object {!r}"
|
||||
.format(type(nic))
|
||||
)
|
||||
# assuming that the MAC address is unique
|
||||
macnics[nic.mac] = nic
|
||||
|
||||
# try configuring all the available adapters
|
||||
adapter_macs = [pair[1] for pair in
|
||||
osutils.get_network_adapters()]
|
||||
if not adapter_macs:
|
||||
raise exception.CloudbaseInitException(
|
||||
"no network adapters available")
|
||||
# configure each one
|
||||
adapter_macs = [pair[1] for pair in network_adapters]
|
||||
reboot_required = False
|
||||
configured = False
|
||||
for mac in adapter_macs:
|
||||
|
@ -1,5 +1,3 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 Cloudbase Solutions Srl
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -15,43 +13,103 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
NAME0 = "eth0"
|
||||
MAC0 = "fa:16:3e:2d:ec:cd"
|
||||
ADDRESS0 = "10.0.0.15"
|
||||
NETMASK0 = "255.255.255.0"
|
||||
BROADCAST0 = "10.0.0.255"
|
||||
GATEWAY0 = "10.0.0.1"
|
||||
DNSNS0 = "208.67.220.220 208.67.222.222"
|
||||
|
||||
NAME1 = "eth1"
|
||||
ADDRESS1 = "10.1.0.2"
|
||||
NETMASK1 = "255.255.255.0"
|
||||
BROADCAST1 = "10.1.0.255"
|
||||
GATEWAY1 = "10.1.0.1"
|
||||
|
||||
|
||||
def get_fake_metadata_json(version):
|
||||
if version == '2013-04-04':
|
||||
return {"random_seed":
|
||||
"Wn51FGjZa3vlZtTxJuPr96oCf+X8jqbA9U2XR5wNdnApy1fz"
|
||||
"/2NNssUwPoNzG6etw9RBn+XiZ0zKWnFzMsTopaN7WwYjWTnIsVw3cpIk"
|
||||
"Td579wQgoEr1ANqhfO3qTvkOVNMhzTAw1ps+wqRmkLxH+1qYJnX06Gcd"
|
||||
"KRRGkWTaOSlTkieA0LO2oTGFlbFDWcOW2vT5BvSBmqP7vNLzbLDMTc7M"
|
||||
"IWRBzwmtcVPC17QL6EhZJTUcZ0mTz7l0R0DocLmFwHEXFEEr+q4WaJjt"
|
||||
"1ejOOxVM3tiT7D8YpRZnnGNPfvEhq1yVMUoi8yv9pFmMmXicNBhm6zDK"
|
||||
"VjcWk0gfbvaQcMnnOLrrE1VxAAzyNyPIXBI/H7AAHz2ECz7dgd2/4ocv"
|
||||
"3bmTRY3hhcUKtNuat2IOvSGgMBUGdWnLorQGFz8t0/bcYhE0Dve35U6H"
|
||||
"mtj78ydV/wmQWG0iq49NX6hk+VUmZtSZztlkbsaa7ajNjZ+Md9oZtlhX"
|
||||
"Z5vJuhRXnHiCm7dRNO8Xo6HffEBH5A4smQ1T2Kda+1c18DZrY7+iQJRi"
|
||||
"fa6witPCw0tXkQ6nlCLqL2weJD1XMiTZLSM/XsZFGGSkKCKvKLEqQrI/"
|
||||
"XFUq/TA6B4aLGFlmmhOO/vMJcht06O8qVU/xtd5Mv/MRFzYaSG568Z/m"
|
||||
"hk4vYLYdQYAA+pXRW9A=",
|
||||
"uuid": "4b32ddf7-7941-4c36-a854-a1f5ac45b318",
|
||||
"availability_zone": "nova",
|
||||
"hostname": "windows.novalocal",
|
||||
"launch_index": 0,
|
||||
"public_keys": {"key": "ssh-rsa "
|
||||
"AAAAB3NzaC1yc2EAAAADAQABAAABA"
|
||||
"QDf7kQHq7zvBod3yIZs0tB/AOOZz5pab7qt/h"
|
||||
"78VF7yi6qTsFdUnQxRue43R/75wa9EEyokgYR"
|
||||
"LKIN+Jq2A5tXNMcK+rNOCzLJFtioAwEl+S6VL"
|
||||
"G9jfkbUv++7zoSMOsanNmEDvG0B79MpyECFCl"
|
||||
"th2DsdE4MQypify35U5ri5Qi7E6PEYAsU65LF"
|
||||
"MG2boeCIB29BEooE6AgPr2DuJeJ+2uw+YScF9"
|
||||
"FV3og4Wyz5zipPVh8YpVev6dlg0tRWUrCtZF9"
|
||||
"IODpCTrT3vsPRG3xz7CppR+vGi/1gLXHtJCRj"
|
||||
"frHwkY6cXyhypNmkU99K/wMqSv30vsDwdnsQ1"
|
||||
"q3YhLarMHB Generated by Nova\n",
|
||||
0: "windows"},
|
||||
"network_config": {"content_path": "network",
|
||||
'debian_config': 'iface eth0 inet static'
|
||||
'address 10.11.12.13'
|
||||
'broadcast 0.0.0.0'
|
||||
'netmask 255.255.255.255'
|
||||
'gateway 1.2.3.4'
|
||||
'dns-nameserver 8.8.8.8'}}
|
||||
data1 = {
|
||||
"random_seed": "Wn51FGjZa3vlZtTxJuPr96oCf+X8jqbA9U2XR5wNdnApy1fz"
|
||||
"/2NNssUwPoNzG6etw9RBn+XiZ0zKWnFzMsTopaN7WwYjWTnI"
|
||||
"sVw3cpIkTd579wQgoEr1ANqhfO3qTvkOVNMhzTAw1ps+wqRm"
|
||||
"kLxH+1qYJnX06GcdKRRGkWTaOSlTkieA0LO2oTGFlbFDWcOW"
|
||||
"2vT5BvSBmqP7vNLzbLDMTc7MIWRBzwmtcVPC17QL6EhZJTUc"
|
||||
"Z0mTz7l0R0DocLmFwHEXFEEr+q4WaJjt1ejOOxVM3tiT7D8Y"
|
||||
"pRZnnGNPfvEhq1yVMUoi8yv9pFmMmXicNBhm6zDKVjcWk0gf"
|
||||
"bvaQcMnnOLrrE1VxAAzyNyPIXBI/H7AAHz2ECz7dgd2/4ocv"
|
||||
"3bmTRY3hhcUKtNuat2IOvSGgMBUGdWnLorQGFz8t0/bcYhE0"
|
||||
"Dve35U6Hmtj78ydV/wmQWG0iq49NX6hk+VUmZtSZztlkbsaa"
|
||||
"7ajNjZ+Md9oZtlhXZ5vJuhRXnHiCm7dRNO8Xo6HffEBH5A4s"
|
||||
"mQ1T2Kda+1c18DZrY7+iQJRifa6witPCw0tXkQ6nlCLqL2we"
|
||||
"JD1XMiTZLSM/XsZFGGSkKCKvKLEqQrI/XFUq/TA6B4aLGFlm"
|
||||
"mhOO/vMJcht06O8qVU/xtd5Mv/MRFzYaSG568Z/mhk4vYLYd"
|
||||
"QYAA+pXRW9A=",
|
||||
"uuid": "4b32ddf7-7941-4c36-a854-a1f5ac45b318",
|
||||
"availability_zone": "nova",
|
||||
"hostname": "windows.novalocal",
|
||||
"launch_index": 0,
|
||||
"public_keys": {
|
||||
"key":
|
||||
"ssh-rsa "
|
||||
"AAAAB3NzaC1yc2EAAAADAQABAAABA"
|
||||
"QDf7kQHq7zvBod3yIZs0tB/AOOZz5pab7qt/h"
|
||||
"78VF7yi6qTsFdUnQxRue43R/75wa9EEyokgYR"
|
||||
"LKIN+Jq2A5tXNMcK+rNOCzLJFtioAwEl+S6VL"
|
||||
"G9jfkbUv++7zoSMOsanNmEDvG0B79MpyECFCl"
|
||||
"th2DsdE4MQypify35U5ri5Qi7E6PEYAsU65LF"
|
||||
"MG2boeCIB29BEooE6AgPr2DuJeJ+2uw+YScF9"
|
||||
"FV3og4Wyz5zipPVh8YpVev6dlg0tRWUrCtZF9"
|
||||
"IODpCTrT3vsPRG3xz7CppR+vGi/1gLXHtJCRj"
|
||||
"frHwkY6cXyhypNmkU99K/wMqSv30vsDwdnsQ1"
|
||||
"q3YhLarMHB Generated by Nova\n",
|
||||
0: "windows"
|
||||
},
|
||||
"network_config": {
|
||||
"content_path": "network",
|
||||
"debian_config": ("""
|
||||
# Injected by Nova on instance boot
|
||||
#
|
||||
# This file describes the network interfaces available on your system
|
||||
# and how to activate them. For more information, see interfaces(5).
|
||||
|
||||
# The loopback network interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
auto {name0}
|
||||
iface {name0} inet static
|
||||
hwaddress ether {mac0}
|
||||
address {address0}
|
||||
netmask {netmask0}
|
||||
broadcast {broadcast0}
|
||||
gateway {gateway0}
|
||||
dns-nameservers {dnsns0}
|
||||
|
||||
auto {name1}
|
||||
iface {name1} inet static
|
||||
address {address1}
|
||||
netmask {netmask1}
|
||||
broadcast {broadcast1}
|
||||
gateway {gateway1}
|
||||
""").format(name0=NAME0, # eth0 (IPv4)
|
||||
mac0=MAC0,
|
||||
address0=ADDRESS0,
|
||||
broadcast0=BROADCAST0,
|
||||
netmask0=NETMASK0,
|
||||
gateway0=GATEWAY0,
|
||||
dnsns0=DNSNS0,
|
||||
# eth1 (IPv4)
|
||||
name1=NAME1,
|
||||
address1=ADDRESS1,
|
||||
broadcast1=BROADCAST1,
|
||||
netmask1=NETMASK1,
|
||||
gateway1=GATEWAY1)
|
||||
}
|
||||
}
|
||||
|
||||
datadict = {
|
||||
"2013-04-04": data1
|
||||
}
|
||||
|
||||
return datadict.get(version)
|
||||
|
@ -1,5 +1,3 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2014 Cloudbase Solutions Srl
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -14,24 +12,41 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
import functools
|
||||
import posixpath
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.metadata.services import baseopenstackservice
|
||||
from cloudbaseinit.tests.metadata import fake_json_response
|
||||
from cloudbaseinit.utils import x509constants
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
MODPATH = "cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
|
||||
|
||||
class TestBaseOpenStackService(unittest.TestCase):
|
||||
|
||||
class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
CONF.set_override('retry_count_interval', 0)
|
||||
CONF.set_override("retry_count_interval", 0)
|
||||
self._service = baseopenstackservice.BaseOpenStackService()
|
||||
date = "2013-04-04"
|
||||
fake_metadata = fake_json_response.get_fake_metadata_json(date)
|
||||
self._fake_network_config = fake_metadata["network_config"]
|
||||
self._fake_content = self._fake_network_config["debian_config"]
|
||||
self._partial_test_get_network_details = functools.partial(
|
||||
self._test_get_network_details,
|
||||
network_config=self._fake_network_config,
|
||||
content=self._fake_content
|
||||
)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_cache_data")
|
||||
def test_get_content(self, mock_get_cache_data):
|
||||
response = self._service.get_content('fake name')
|
||||
@ -39,7 +54,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
mock_get_cache_data.assert_called_once_with(path)
|
||||
self.assertEqual(mock_get_cache_data.return_value, response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_cache_data")
|
||||
def test_get_user_data(self, mock_get_cache_data):
|
||||
response = self._service.get_user_data()
|
||||
@ -47,7 +62,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
mock_get_cache_data.assert_called_once_with(path)
|
||||
self.assertEqual(mock_get_cache_data.return_value, response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_cache_data")
|
||||
def test_get_meta_data(self, mock_get_cache_data):
|
||||
mock_get_cache_data.return_value = b'{"fake": "data"}'
|
||||
@ -57,7 +72,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
mock_get_cache_data.assert_called_with(path)
|
||||
self.assertEqual({"fake": "data"}, response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def test_get_instance_id(self, mock_get_meta_data):
|
||||
response = self._service.get_instance_id()
|
||||
@ -66,7 +81,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
self.assertEqual(mock_get_meta_data.return_value.get.return_value,
|
||||
response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def test_get_host_name(self, mock_get_meta_data):
|
||||
response = self._service.get_host_name()
|
||||
@ -75,7 +90,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
self.assertEqual(mock_get_meta_data.return_value.get.return_value,
|
||||
response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def test_get_public_keys(self, mock_get_meta_data):
|
||||
response = self._service.get_public_keys()
|
||||
@ -83,15 +98,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
mock_get_meta_data().get.assert_called_once_with('public_keys')
|
||||
self.assertEqual(mock_get_meta_data().get().values(), response)
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def test_get_network_config(self, mock_get_meta_data):
|
||||
response = self._service.get_network_config()
|
||||
mock_get_meta_data.assert_called_once_with()
|
||||
mock_get_meta_data().get.assert_called_once_with('network_config')
|
||||
self.assertEqual(response, mock_get_meta_data().get())
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def _test_get_admin_password(self, mock_get_meta_data, meta_data):
|
||||
mock_get_meta_data.return_value = meta_data
|
||||
@ -102,7 +109,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
elif meta_data and 'admin_pass' in meta_data.get('meta'):
|
||||
self.assertEqual(meta_data.get('meta')['admin_pass'], response)
|
||||
else:
|
||||
self.assertEqual(None, response)
|
||||
self.assertIsNone(response)
|
||||
|
||||
def test_get_admin_pass(self):
|
||||
self._test_get_admin_password(meta_data={'admin_pass': 'fake pass'})
|
||||
@ -114,9 +121,9 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
def test_get_admin_pass_no_pass(self):
|
||||
self._test_get_admin_password(meta_data={})
|
||||
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService.get_user_data")
|
||||
def _test_get_client_auth_certs(self, mock_get_user_data,
|
||||
mock_get_meta_data, meta_data,
|
||||
@ -132,7 +139,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
mock_get_user_data.assert_called_once_with()
|
||||
self.assertEqual([ret_value], response)
|
||||
elif ret_value is base.NotExistingMetadataException:
|
||||
self.assertEqual(None, response)
|
||||
self.assertIsNone(response)
|
||||
|
||||
def test_get_client_auth_certs(self):
|
||||
self._test_get_client_auth_certs(
|
||||
@ -145,3 +152,75 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
||||
def test_get_client_auth_certs_no_cert_data_exception(self):
|
||||
self._test_get_client_auth_certs(
|
||||
meta_data={}, ret_value=base.NotExistingMetadataException)
|
||||
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService.get_content")
|
||||
@mock.patch(MODPATH +
|
||||
".BaseOpenStackService._get_meta_data")
|
||||
def _test_get_network_details(self,
|
||||
mock_get_meta_data,
|
||||
mock_get_content,
|
||||
network_config=None,
|
||||
content=None,
|
||||
search_fail=False,
|
||||
no_path=False):
|
||||
# mock obtained data
|
||||
mock_get_meta_data().get.return_value = network_config
|
||||
mock_get_content.return_value = content
|
||||
# actual tests
|
||||
if search_fail:
|
||||
ret = self._service.get_network_details()
|
||||
self.assertFalse(ret)
|
||||
return
|
||||
ret = self._service.get_network_details()
|
||||
mock_get_meta_data().get.assert_called_once_with("network_config")
|
||||
if network_config and not no_path:
|
||||
mock_get_content.assert_called_once_with("network")
|
||||
if not network_config:
|
||||
self.assertIsNone(ret)
|
||||
return
|
||||
if no_path:
|
||||
self.assertIsNone(ret)
|
||||
return
|
||||
# check returned NICs details
|
||||
nic1 = base.NetworkDetails(
|
||||
fake_json_response.NAME0,
|
||||
fake_json_response.MAC0.upper(),
|
||||
fake_json_response.ADDRESS0,
|
||||
fake_json_response.NETMASK0,
|
||||
fake_json_response.BROADCAST0,
|
||||
fake_json_response.GATEWAY0,
|
||||
fake_json_response.DNSNS0.split()
|
||||
)
|
||||
nic2 = base.NetworkDetails(
|
||||
fake_json_response.NAME1,
|
||||
None,
|
||||
fake_json_response.ADDRESS1,
|
||||
fake_json_response.NETMASK1,
|
||||
fake_json_response.BROADCAST1,
|
||||
fake_json_response.GATEWAY1,
|
||||
None
|
||||
)
|
||||
self.assertEqual([nic1, nic2], ret)
|
||||
|
||||
def test_get_network_details_no_config(self):
|
||||
self._partial_test_get_network_details(
|
||||
network_config=None
|
||||
)
|
||||
|
||||
def test_get_network_details_no_path(self):
|
||||
self._fake_network_config.pop("content_path", None)
|
||||
self._partial_test_get_network_details(
|
||||
network_config=self._fake_network_config,
|
||||
no_path=True
|
||||
)
|
||||
|
||||
def test_get_network_details_search_fail(self):
|
||||
self._fake_content = "invalid format"
|
||||
self._partial_test_get_network_details(
|
||||
content=self._fake_content,
|
||||
search_fail=True
|
||||
)
|
||||
|
||||
def test_get_network_details(self):
|
||||
self._partial_test_get_network_details()
|
||||
|
@ -97,8 +97,9 @@ ETH1_MAC='{mac}'
|
||||
)
|
||||
|
||||
|
||||
def _get_nic_details():
|
||||
def _get_nic_details(iid=0):
|
||||
details = base.NetworkDetails(
|
||||
opennebulaservice.IF_FORMAT.format(iid=iid),
|
||||
MAC,
|
||||
ADDRESS,
|
||||
NETMASK,
|
||||
@ -302,8 +303,9 @@ class TestLoadedOpenNebulaService(_TestOpenNebulaService):
|
||||
|
||||
def test_multiple_nics(self):
|
||||
self.load_context(context=CONTEXT2)
|
||||
details = _get_nic_details()
|
||||
network_details = [details] * 2
|
||||
nic1 = _get_nic_details(iid=0)
|
||||
nic2 = _get_nic_details(iid=1)
|
||||
network_details = [nic1, nic2]
|
||||
self.assertEqual(
|
||||
network_details,
|
||||
self._service.get_network_details()
|
||||
|
@ -13,163 +13,226 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import re
|
||||
import functools
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.metadata.services import base as service_base
|
||||
from cloudbaseinit.plugins import base as plugin_base
|
||||
from cloudbaseinit.plugins.windows import networkconfig
|
||||
from cloudbaseinit.tests.metadata import fake_json_response
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestNetworkConfigPlugin(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._network_plugin = networkconfig.NetworkConfigPlugin()
|
||||
self.fake_data = fake_json_response.get_fake_metadata_json(
|
||||
'2013-04-04')
|
||||
self._setUp()
|
||||
|
||||
@mock.patch('cloudbaseinit.osutils.factory.get_os_utils')
|
||||
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
|
||||
def _test_execute(self, mock_get_os_utils,
|
||||
search_result=mock.MagicMock(),
|
||||
no_adapter_name=False, no_adapters=False,
|
||||
using_content=0, details_list=None,
|
||||
missing_content_path=False):
|
||||
fake_adapter = ("fake_name_0", "fake_mac_0")
|
||||
network_adapters=None,
|
||||
network_details=None,
|
||||
invalid_details=False,
|
||||
missed_adapters=[],
|
||||
extra_network_details=[]):
|
||||
# prepare mock environment
|
||||
mock_service = mock.MagicMock()
|
||||
mock_shared_data = mock.Mock()
|
||||
mock_osutils = mock.MagicMock()
|
||||
mock_ndetails = mock.Mock()
|
||||
re.search = mock.MagicMock(return_value=search_result)
|
||||
fake_shared_data = 'fake shared data'
|
||||
network_config = self.fake_data['network_config']
|
||||
if not details_list:
|
||||
details_list = [None] * 6
|
||||
details_list[0] = fake_adapter[1] # set MAC for matching
|
||||
if no_adapter_name: # nothing provided in the config file
|
||||
CONF.set_override("network_adapter", None)
|
||||
else:
|
||||
CONF.set_override("network_adapter", fake_adapter[0])
|
||||
mock_osutils.get_network_adapters.return_value = [
|
||||
fake_adapter,
|
||||
# and other adapters
|
||||
("name1", "mac1"),
|
||||
("name2", "mac2")
|
||||
]
|
||||
mock_service.get_network_details.return_value = network_details
|
||||
mock_get_os_utils.return_value = mock_osutils
|
||||
mock_osutils.set_static_network_config.return_value = False
|
||||
# service method setup
|
||||
methods = ["get_network_config", "get_content", "get_network_details"]
|
||||
for method in methods:
|
||||
mock_method = getattr(mock_service, method)
|
||||
mock_method.return_value = None
|
||||
if using_content == 1:
|
||||
mock_service.get_network_config.return_value = network_config
|
||||
mock_service.get_content.return_value = search_result
|
||||
|
||||
elif using_content == 2:
|
||||
mock_service.get_network_details.return_value = [mock_ndetails]
|
||||
mock_osutils.get_network_adapters.return_value = network_adapters
|
||||
mock_osutils.set_static_network_config.return_value = True
|
||||
network_execute = functools.partial(
|
||||
self._network_plugin.execute,
|
||||
mock_service, mock_shared_data
|
||||
)
|
||||
# actual tests
|
||||
if search_result is None and using_content == 1:
|
||||
self.assertRaises(exception.CloudbaseInitException,
|
||||
self._network_plugin.execute,
|
||||
mock_service, fake_shared_data)
|
||||
if not network_details:
|
||||
ret = network_execute()
|
||||
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, False), ret)
|
||||
return
|
||||
if no_adapters:
|
||||
mock_osutils.get_network_adapters.return_value = []
|
||||
self.assertRaises(exception.CloudbaseInitException,
|
||||
self._network_plugin.execute,
|
||||
mock_service, fake_shared_data)
|
||||
return
|
||||
attrs = [
|
||||
"address",
|
||||
"netmask",
|
||||
"broadcast",
|
||||
"gateway",
|
||||
"dnsnameservers",
|
||||
]
|
||||
if using_content == 0:
|
||||
response = self._network_plugin.execute(mock_service,
|
||||
fake_shared_data)
|
||||
elif using_content == 1:
|
||||
if missing_content_path:
|
||||
mock_service.get_network_config.return_value.pop(
|
||||
"content_path", None
|
||||
)
|
||||
response = self._network_plugin.execute(mock_service,
|
||||
fake_shared_data)
|
||||
if not missing_content_path:
|
||||
mock_service.get_network_config.assert_called_once_with()
|
||||
mock_service.get_content.assert_called_once_with(
|
||||
network_config['content_path'])
|
||||
adapters = mock_osutils.get_network_adapters()
|
||||
if CONF.network_adapter:
|
||||
mac = [pair[1] for pair in adapters
|
||||
if pair == fake_adapter][0]
|
||||
else:
|
||||
mac = adapters[0][1]
|
||||
(
|
||||
address,
|
||||
netmask,
|
||||
broadcast,
|
||||
gateway,
|
||||
dnsnameserver
|
||||
) = map(search_result.group, attrs)
|
||||
dnsnameservers = dnsnameserver.strip().split(" ")
|
||||
elif using_content == 2:
|
||||
if invalid_details:
|
||||
with self.assertRaises(exception.CloudbaseInitException):
|
||||
self._network_plugin.execute(mock_service,
|
||||
fake_shared_data)
|
||||
mock_service.get_network_details.reset_mock()
|
||||
mock_ndetails = service_base.NetworkDetails(*details_list)
|
||||
mock_service.get_network_details.return_value = [mock_ndetails]
|
||||
response = self._network_plugin.execute(mock_service,
|
||||
fake_shared_data)
|
||||
mock_service.get_network_details.assert_called_once_with()
|
||||
mac = mock_ndetails.mac
|
||||
(
|
||||
address,
|
||||
netmask,
|
||||
broadcast,
|
||||
gateway,
|
||||
dnsnameservers
|
||||
) = map(lambda attr: getattr(mock_ndetails, attr), attrs)
|
||||
if using_content in (1, 2) and not missing_content_path:
|
||||
mock_osutils.set_static_network_config.assert_called_once_with(
|
||||
mac,
|
||||
address,
|
||||
netmask,
|
||||
broadcast,
|
||||
gateway,
|
||||
dnsnameservers
|
||||
network_execute()
|
||||
return
|
||||
if not network_adapters:
|
||||
with self.assertRaises(exception.CloudbaseInitException):
|
||||
network_execute()
|
||||
return
|
||||
# good to go for the configuration process
|
||||
ret = network_execute()
|
||||
calls = []
|
||||
for adapter in set(network_adapters) - set(missed_adapters):
|
||||
nics = [nic for nic in (network_details +
|
||||
extra_network_details)
|
||||
if nic.mac == adapter[1]]
|
||||
self.assertTrue(nics) # missed_adapters should do the job
|
||||
nic = nics[0]
|
||||
call = mock.call(
|
||||
nic.mac,
|
||||
nic.address,
|
||||
nic.netmask,
|
||||
nic.broadcast,
|
||||
nic.gateway,
|
||||
nic.dnsnameservers
|
||||
)
|
||||
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, False),
|
||||
response)
|
||||
calls.append(call)
|
||||
mock_osutils.set_static_network_config.assert_has_calls(
|
||||
calls, any_order=True)
|
||||
reboot = len(missed_adapters) != self._count
|
||||
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, reboot), ret)
|
||||
|
||||
def test_execute(self):
|
||||
self._test_execute(using_content=1)
|
||||
def _setUp(self, same_names=True):
|
||||
# Generate fake pairs of NetworkDetails objects and
|
||||
# local ethernet network adapters.
|
||||
self._count = 3
|
||||
details_names = [
|
||||
"eth0",
|
||||
"eth1",
|
||||
"eth2"
|
||||
]
|
||||
if same_names:
|
||||
adapters_names = details_names[:]
|
||||
else:
|
||||
adapters_names = ["vm " + name for name in details_names]
|
||||
macs = [
|
||||
"54:EE:75:19:F4:61",
|
||||
"54:EE:75:19:F4:62",
|
||||
"54:EE:75:19:F4:63"
|
||||
]
|
||||
addresses = [
|
||||
"192.168.122.101",
|
||||
"192.168.103.104",
|
||||
"192.168.122.105",
|
||||
]
|
||||
netmasks = [
|
||||
"255.255.255.0",
|
||||
"255.255.0.0",
|
||||
"255.255.255.128",
|
||||
]
|
||||
broadcasts = [
|
||||
"192.168.122.255",
|
||||
"192.168.255.255",
|
||||
"192.168.122.127",
|
||||
]
|
||||
gateway = [
|
||||
"192.168.122.1",
|
||||
"192.168.122.16",
|
||||
"192.168.122.32",
|
||||
]
|
||||
dnsnses = [
|
||||
"8.8.8.8",
|
||||
"8.8.8.8 8.8.4.4",
|
||||
"8.8.8.8 0.0.0.0",
|
||||
]
|
||||
self._network_adapters = []
|
||||
self._network_details = []
|
||||
for ind in range(self._count):
|
||||
adapter = (adapters_names[ind], macs[ind])
|
||||
nic = service_base.NetworkDetails(
|
||||
details_names[ind],
|
||||
macs[ind],
|
||||
addresses[ind],
|
||||
netmasks[ind],
|
||||
broadcasts[ind],
|
||||
gateway[ind],
|
||||
dnsnses[ind].split()
|
||||
)
|
||||
self._network_adapters.append(adapter)
|
||||
self._network_details.append(nic)
|
||||
# get the network config plugin
|
||||
self._network_plugin = networkconfig.NetworkConfigPlugin()
|
||||
# execution wrapper
|
||||
self._partial_test_execute = functools.partial(
|
||||
self._test_execute,
|
||||
network_adapters=self._network_adapters,
|
||||
network_details=self._network_details
|
||||
)
|
||||
|
||||
def test_execute_missing_content_path(self):
|
||||
self._test_execute(using_content=1, missing_content_path=True)
|
||||
def test_execute_no_network_details(self):
|
||||
self._network_details[:] = []
|
||||
self._partial_test_execute()
|
||||
|
||||
def test_execute_no_debian(self):
|
||||
self._test_execute(search_result=None, using_content=1)
|
||||
def test_execute_no_network_adapters(self):
|
||||
self._network_adapters[:] = []
|
||||
self._partial_test_execute()
|
||||
|
||||
def test_execute_no_adapter_name(self):
|
||||
self._test_execute(no_adapter_name=True, using_content=1)
|
||||
def test_execute_invalid_network_details(self):
|
||||
self._network_details.append([None] * 6)
|
||||
self._partial_test_execute(invalid_details=True)
|
||||
|
||||
def test_execute_no_adapter_name_or_adapters(self):
|
||||
self._test_execute(no_adapter_name=True, no_adapters=True,
|
||||
using_content=1)
|
||||
def test_execute_single(self):
|
||||
for _ in range(self._count - 1):
|
||||
self._network_adapters.pop()
|
||||
self._network_details.pop()
|
||||
self._partial_test_execute()
|
||||
|
||||
def test_execute_network_details(self):
|
||||
self._test_execute(using_content=2)
|
||||
def test_execute_multiple(self):
|
||||
self._partial_test_execute()
|
||||
|
||||
def test_execute_no_config_or_details(self):
|
||||
self._test_execute(using_content=0)
|
||||
def test_execute_missing_one(self):
|
||||
self.assertGreater(self._count, 1)
|
||||
self._network_details.pop(0)
|
||||
adapter = self._network_adapters[0]
|
||||
self._partial_test_execute(missed_adapters=[adapter])
|
||||
|
||||
def test_execute_missing_all(self):
|
||||
nic = self._network_details[0]
|
||||
nic = service_base.NetworkDetails(
|
||||
nic.name,
|
||||
"00" + nic.mac[2:],
|
||||
nic.address,
|
||||
nic.netmask,
|
||||
nic.broadcast,
|
||||
nic.gateway,
|
||||
nic.dnsnameservers
|
||||
)
|
||||
self._network_details[:] = [nic]
|
||||
self._partial_test_execute(missed_adapters=self._network_adapters)
|
||||
|
||||
def _test_execute_missing_smth(self, name=False, mac=False,
|
||||
address=False, gateway=False,
|
||||
fail=False):
|
||||
ind = self._count - 1
|
||||
nic = self._network_details[ind]
|
||||
nic2 = service_base.NetworkDetails(
|
||||
None if name else nic.name,
|
||||
None if mac else nic.mac,
|
||||
None if address else nic.address,
|
||||
nic.netmask,
|
||||
nic.broadcast,
|
||||
None if gateway else nic.gateway,
|
||||
nic.dnsnameservers
|
||||
)
|
||||
self._network_details[ind] = nic2
|
||||
# excluding address and gateway switches...
|
||||
if not fail:
|
||||
# even this way, all adapters should be configured
|
||||
missed_adapters = []
|
||||
extra_network_details = [nic]
|
||||
else:
|
||||
# both name and MAC are missing, so we can't make the match
|
||||
missed_adapters = [self._network_adapters[ind]]
|
||||
extra_network_details = []
|
||||
self._partial_test_execute(
|
||||
missed_adapters=missed_adapters,
|
||||
extra_network_details=extra_network_details
|
||||
)
|
||||
|
||||
def test_execute_missing_mac(self):
|
||||
self._test_execute_missing_smth(mac=True)
|
||||
|
||||
def test_execute_missing_mac2(self):
|
||||
self._setUp(same_names=False)
|
||||
self._test_execute_missing_smth(mac=True)
|
||||
|
||||
def test_execute_missing_name_mac(self):
|
||||
self._test_execute_missing_smth(name=True, mac=True, fail=True)
|
||||
|
||||
def test_execute_missing_address(self):
|
||||
self._test_execute_missing_smth(address=True, fail=True)
|
||||
|
||||
def test_execute_missing_gateway(self):
|
||||
self._test_execute_missing_smth(gateway=True)
|
||||
|
63
cloudbaseinit/tests/utils/test_debiface.py
Normal file
63
cloudbaseinit/tests/utils/test_debiface.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright 2014 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from cloudbaseinit.metadata.services import base as service_base
|
||||
from cloudbaseinit.tests.metadata import fake_json_response
|
||||
from cloudbaseinit.utils import debiface
|
||||
|
||||
|
||||
class TestInterfacesParser(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
date = "2013-04-04"
|
||||
content = fake_json_response.get_fake_metadata_json(date)
|
||||
self.data = content["network_config"]["debian_config"]
|
||||
|
||||
def _test_parse_nics(self, no_nics=False):
|
||||
nics = debiface.parse(self.data)
|
||||
if no_nics:
|
||||
self.assertFalse(nics)
|
||||
return
|
||||
# check what we've got
|
||||
nic0 = service_base.NetworkDetails(
|
||||
fake_json_response.NAME0,
|
||||
fake_json_response.MAC0.upper(),
|
||||
fake_json_response.ADDRESS0,
|
||||
fake_json_response.NETMASK0,
|
||||
fake_json_response.BROADCAST0,
|
||||
fake_json_response.GATEWAY0,
|
||||
fake_json_response.DNSNS0.split()
|
||||
)
|
||||
nic1 = service_base.NetworkDetails(
|
||||
fake_json_response.NAME1,
|
||||
None,
|
||||
fake_json_response.ADDRESS1,
|
||||
fake_json_response.NETMASK1,
|
||||
fake_json_response.BROADCAST1,
|
||||
fake_json_response.GATEWAY1,
|
||||
None
|
||||
)
|
||||
self.assertEqual([nic0, nic1], nics)
|
||||
|
||||
def test_nothing_to_parse(self):
|
||||
invalid = [None, "", 324242, ("dasd", "dsa")]
|
||||
for data in invalid:
|
||||
self.data = data
|
||||
self._test_parse_nics(no_nics=True)
|
||||
|
||||
def test_parse(self):
|
||||
self._test_parse_nics()
|
100
cloudbaseinit/utils/debiface.py
Normal file
100
cloudbaseinit/utils/debiface.py
Normal file
@ -0,0 +1,100 @@
|
||||
# Copyright 2014 Cloudbase Solutions Srl
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import re
|
||||
|
||||
import six
|
||||
|
||||
from cloudbaseinit.metadata.services import base as service_base
|
||||
from cloudbaseinit.openstack.common import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
NAME = "name"
|
||||
MAC = "mac"
|
||||
ADDRESS = "address"
|
||||
NETMASK = "netmask"
|
||||
BROADCAST = "broadcast"
|
||||
GATEWAY = "gateway"
|
||||
DNSNS = "dnsnameservers"
|
||||
# fields of interest (order and regexp)
|
||||
FIELDS = {
|
||||
NAME: (0, re.compile(r"iface\s+(?P<{}>\S+)"
|
||||
r"\s+inet\s+static".format(NAME))),
|
||||
MAC: (1, re.compile(r"hwaddress\s+ether\s+"
|
||||
r"(?P<{}>\S+)".format(MAC))),
|
||||
ADDRESS: (2, re.compile(r"address\s+"
|
||||
r"(?P<{}>\S+)".format(ADDRESS))),
|
||||
NETMASK: (3, re.compile(r"netmask\s+"
|
||||
r"(?P<{}>\S+)".format(NETMASK))),
|
||||
BROADCAST: (4, re.compile(r"broadcast\s+"
|
||||
r"(?P<{}>\S+)".format(BROADCAST))),
|
||||
GATEWAY: (5, re.compile(r"gateway\s+"
|
||||
r"(?P<{}>\S+)".format(GATEWAY))),
|
||||
DNSNS: (6, re.compile(r"dns-nameservers\s+(?P<{}>.+)".format(DNSNS)))
|
||||
}
|
||||
IFACE_TEMPLATE = dict.fromkeys(range(len(FIELDS)))
|
||||
|
||||
|
||||
def _get_field(line):
|
||||
for field, (index, regex) in FIELDS.items():
|
||||
match = regex.match(line)
|
||||
if match:
|
||||
return index, match.group(field)
|
||||
|
||||
|
||||
def _add_nic(iface, nics):
|
||||
if not iface:
|
||||
return
|
||||
details = [iface[key] for key in sorted(iface)]
|
||||
LOG.debug("Found new interface: %s", details)
|
||||
# each missing detail is marked as None
|
||||
nic = service_base.NetworkDetails(*details)
|
||||
nics.append(nic)
|
||||
|
||||
|
||||
def parse(data):
|
||||
"""Parse the received content and obtain network details."""
|
||||
# TODO(cpoieana): support IPv6 flavors
|
||||
if not data or not isinstance(data, six.string_types):
|
||||
LOG.error("Invalid debian config to parse:\n%s", data)
|
||||
return
|
||||
LOG.info("Parsing debian config...\n%s", data)
|
||||
nics = [] # list of NetworkDetails objects
|
||||
iface = {}
|
||||
# take each line and process it
|
||||
for line in data.split("\n"):
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
ret = _get_field(line)
|
||||
if not ret:
|
||||
continue
|
||||
# save the detail
|
||||
index = ret[0]
|
||||
if index == 0:
|
||||
# we found a new interface
|
||||
_add_nic(iface, nics)
|
||||
iface = IFACE_TEMPLATE.copy()
|
||||
value = ret[1]
|
||||
if index == 1:
|
||||
value = value.upper()
|
||||
elif index == 6:
|
||||
value = value.strip().split()
|
||||
iface[index] = value
|
||||
# also add the last one
|
||||
_add_nic(iface, nics)
|
||||
return nics
|
Loading…
x
Reference in New Issue
Block a user