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 abc
|
||||||
import collections
|
import collections
|
||||||
import time
|
import time
|
||||||
import warnings
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
@ -42,6 +41,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
NetworkDetails = collections.namedtuple(
|
NetworkDetails = collections.namedtuple(
|
||||||
"NetworkDetails",
|
"NetworkDetails",
|
||||||
[
|
[
|
||||||
|
"name",
|
||||||
"mac",
|
"mac",
|
||||||
"address",
|
"address",
|
||||||
"netmask",
|
"netmask",
|
||||||
@ -98,7 +98,7 @@ class BaseMetadataService(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_content(self, name):
|
def get_content(self, name):
|
||||||
pass
|
"""Get raw content within a service."""
|
||||||
|
|
||||||
def get_user_data(self):
|
def get_user_data(self):
|
||||||
pass
|
pass
|
||||||
@ -109,11 +109,6 @@ class BaseMetadataService(object):
|
|||||||
def get_public_keys(self):
|
def get_public_keys(self):
|
||||||
pass
|
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):
|
def get_network_details(self):
|
||||||
"""Return a list of `NetworkDetails` objects.
|
"""Return a list of `NetworkDetails` objects.
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import posixpath
|
import posixpath
|
||||||
|
|
||||||
@ -19,9 +20,11 @@ from oslo.config import cfg
|
|||||||
|
|
||||||
from cloudbaseinit.metadata.services import base
|
from cloudbaseinit.metadata.services import base
|
||||||
from cloudbaseinit.openstack.common import log as logging
|
from cloudbaseinit.openstack.common import log as logging
|
||||||
|
from cloudbaseinit.utils import debiface
|
||||||
from cloudbaseinit.utils import encoding
|
from cloudbaseinit.utils import encoding
|
||||||
from cloudbaseinit.utils import x509constants
|
from cloudbaseinit.utils import x509constants
|
||||||
|
|
||||||
|
|
||||||
opts = [
|
opts = [
|
||||||
cfg.StrOpt('metadata_base_url', default='http://169.254.169.254/',
|
cfg.StrOpt('metadata_base_url', default='http://169.254.169.254/',
|
||||||
help='The base URL where the service looks for metadata'),
|
help='The base URL where the service looks for metadata'),
|
||||||
@ -63,8 +66,19 @@ class BaseOpenStackService(base.BaseMetadataService):
|
|||||||
if public_keys:
|
if public_keys:
|
||||||
return public_keys.values()
|
return public_keys.values()
|
||||||
|
|
||||||
def get_network_config(self):
|
def get_network_details(self):
|
||||||
return self._get_meta_data().get('network_config')
|
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):
|
def get_admin_password(self):
|
||||||
meta_data = self._get_meta_data()
|
meta_data = self._get_meta_data()
|
||||||
|
@ -101,6 +101,6 @@ class EC2Service(base.BaseMetadataService):
|
|||||||
|
|
||||||
return ssh_keys
|
return ssh_keys
|
||||||
|
|
||||||
def get_network_config(self):
|
def get_network_details(self):
|
||||||
# TODO(alexpilotti): add static network support
|
# TODO(cpoieana): add static network config support
|
||||||
pass
|
pass
|
||||||
|
@ -31,6 +31,8 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
CONTEXT_FILE = "context.sh"
|
CONTEXT_FILE = "context.sh"
|
||||||
INSTANCE_ID = "iid-dsopennebula"
|
INSTANCE_ID = "iid-dsopennebula"
|
||||||
|
# interface name default template
|
||||||
|
IF_FORMAT = "eth{iid}"
|
||||||
|
|
||||||
# metadata identifiers
|
# metadata identifiers
|
||||||
HOST_NAME = ["SET_HOSTNAME", "HOSTNAME"]
|
HOST_NAME = ["SET_HOSTNAME", "HOSTNAME"]
|
||||||
@ -211,15 +213,21 @@ class OpenNebulaService(base.BaseMetadataService):
|
|||||||
# get existing values
|
# get existing values
|
||||||
mac = self._get_cache_data(MAC, iid=iid).upper()
|
mac = self._get_cache_data(MAC, iid=iid).upper()
|
||||||
address = self._get_cache_data(ADDRESS, iid=iid)
|
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 to find/predict and compute the rest
|
||||||
|
try:
|
||||||
|
gateway = self._get_cache_data(GATEWAY, iid=iid)
|
||||||
|
except base.NotExistingMetadataException:
|
||||||
|
gateway = None
|
||||||
try:
|
try:
|
||||||
netmask = self._get_cache_data(NETMASK, iid=iid)
|
netmask = self._get_cache_data(NETMASK, iid=iid)
|
||||||
except base.NotExistingMetadataException:
|
except base.NotExistingMetadataException:
|
||||||
|
if not gateway:
|
||||||
|
raise
|
||||||
netmask = self._calculate_netmask(address, gateway)
|
netmask = self._calculate_netmask(address, gateway)
|
||||||
broadcast = self._compute_broadcast(address, netmask)
|
broadcast = self._compute_broadcast(address, netmask)
|
||||||
# gather them as namedtuple objects
|
# gather them as namedtuple objects
|
||||||
details = base.NetworkDetails(
|
details = base.NetworkDetails(
|
||||||
|
IF_FORMAT.format(iid=iid),
|
||||||
mac,
|
mac,
|
||||||
address,
|
address,
|
||||||
netmask,
|
netmask,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes import wintypes
|
from ctypes import wintypes
|
||||||
import os
|
import os
|
||||||
@ -541,19 +542,21 @@ class WindowsUtils(base.BaseOSUtils):
|
|||||||
"Cannot set static IP address on network adapter")
|
"Cannot set static IP address on network adapter")
|
||||||
reboot_required = (ret_val == 1)
|
reboot_required = (ret_val == 1)
|
||||||
|
|
||||||
LOG.debug("Setting static gateways")
|
if gateway:
|
||||||
(ret_val,) = adapter_config.SetGateways([gateway], [1])
|
LOG.debug("Setting static gateways")
|
||||||
if ret_val > 1:
|
(ret_val,) = adapter_config.SetGateways([gateway], [1])
|
||||||
raise exception.CloudbaseInitException(
|
if ret_val > 1:
|
||||||
"Cannot set gateway on network adapter")
|
raise exception.CloudbaseInitException(
|
||||||
reboot_required = reboot_required or ret_val == 1
|
"Cannot set gateway on network adapter")
|
||||||
|
reboot_required = reboot_required or ret_val == 1
|
||||||
|
|
||||||
LOG.debug("Setting static DNS servers")
|
if dnsnameservers:
|
||||||
(ret_val,) = adapter_config.SetDNSServerSearchOrder(dnsnameservers)
|
LOG.debug("Setting static DNS servers")
|
||||||
if ret_val > 1:
|
(ret_val,) = adapter_config.SetDNSServerSearchOrder(dnsnameservers)
|
||||||
raise exception.CloudbaseInitException(
|
if ret_val > 1:
|
||||||
"Cannot set DNS on network adapter")
|
raise exception.CloudbaseInitException(
|
||||||
reboot_required = reboot_required or ret_val == 1
|
"Cannot set DNS on network adapter")
|
||||||
|
reboot_required = reboot_required or ret_val == 1
|
||||||
|
|
||||||
return reboot_required
|
return reboot_required
|
||||||
|
|
||||||
|
@ -13,111 +13,102 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from oslo.config import cfg
|
|
||||||
|
|
||||||
from cloudbaseinit import exception
|
from cloudbaseinit import exception
|
||||||
from cloudbaseinit.metadata.services import base as service_base
|
from cloudbaseinit.metadata.services import base as service_base
|
||||||
from cloudbaseinit.openstack.common import log as logging
|
from cloudbaseinit.openstack.common import log as logging
|
||||||
from cloudbaseinit.osutils import factory as osutils_factory
|
from cloudbaseinit.osutils import factory as osutils_factory
|
||||||
from cloudbaseinit.plugins import base as plugin_base
|
from cloudbaseinit.plugins import base as plugin_base
|
||||||
from cloudbaseinit.utils import encoding
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
opts = [
|
# Mandatory network details are marked with True. And
|
||||||
cfg.StrOpt('network_adapter', default=None, help='Network adapter to '
|
# if the key is a tuple, then at least one field must exists.
|
||||||
'configure. If not specified, the first available ethernet '
|
NET_REQUIRE = {
|
||||||
'adapter will be chosen.'),
|
("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):
|
class NetworkConfigPlugin(plugin_base.BasePlugin):
|
||||||
|
|
||||||
def execute(self, service, shared_data):
|
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()
|
osutils = osutils_factory.get_os_utils()
|
||||||
network_details = service.get_network_details()
|
network_details = service.get_network_details()
|
||||||
if not network_details:
|
if not network_details:
|
||||||
network_config = service.get_network_config()
|
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
||||||
if not network_config:
|
|
||||||
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
|
||||||
|
|
||||||
# ---- BEGIN deprecated code ----
|
# check and save NICs by MAC
|
||||||
if not network_details:
|
network_adapters = osutils.get_network_adapters()
|
||||||
if 'content_path' not in network_config:
|
network_details = _preprocess_nics(network_details,
|
||||||
return (plugin_base.PLUGIN_EXECUTION_DONE, False)
|
network_adapters)
|
||||||
|
|
||||||
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
|
|
||||||
macnics = {}
|
macnics = {}
|
||||||
for nic in network_details:
|
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
|
# assuming that the MAC address is unique
|
||||||
macnics[nic.mac] = nic
|
macnics[nic.mac] = nic
|
||||||
|
|
||||||
# try configuring all the available adapters
|
# try configuring all the available adapters
|
||||||
adapter_macs = [pair[1] for pair in
|
adapter_macs = [pair[1] for pair in network_adapters]
|
||||||
osutils.get_network_adapters()]
|
|
||||||
if not adapter_macs:
|
|
||||||
raise exception.CloudbaseInitException(
|
|
||||||
"no network adapters available")
|
|
||||||
# configure each one
|
|
||||||
reboot_required = False
|
reboot_required = False
|
||||||
configured = False
|
configured = False
|
||||||
for mac in adapter_macs:
|
for mac in adapter_macs:
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2013 Cloudbase Solutions Srl
|
# Copyright 2013 Cloudbase Solutions Srl
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -15,43 +13,103 @@
|
|||||||
# under the License.
|
# 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):
|
def get_fake_metadata_json(version):
|
||||||
if version == '2013-04-04':
|
data1 = {
|
||||||
return {"random_seed":
|
"random_seed": "Wn51FGjZa3vlZtTxJuPr96oCf+X8jqbA9U2XR5wNdnApy1fz"
|
||||||
"Wn51FGjZa3vlZtTxJuPr96oCf+X8jqbA9U2XR5wNdnApy1fz"
|
"/2NNssUwPoNzG6etw9RBn+XiZ0zKWnFzMsTopaN7WwYjWTnI"
|
||||||
"/2NNssUwPoNzG6etw9RBn+XiZ0zKWnFzMsTopaN7WwYjWTnIsVw3cpIk"
|
"sVw3cpIkTd579wQgoEr1ANqhfO3qTvkOVNMhzTAw1ps+wqRm"
|
||||||
"Td579wQgoEr1ANqhfO3qTvkOVNMhzTAw1ps+wqRmkLxH+1qYJnX06Gcd"
|
"kLxH+1qYJnX06GcdKRRGkWTaOSlTkieA0LO2oTGFlbFDWcOW"
|
||||||
"KRRGkWTaOSlTkieA0LO2oTGFlbFDWcOW2vT5BvSBmqP7vNLzbLDMTc7M"
|
"2vT5BvSBmqP7vNLzbLDMTc7MIWRBzwmtcVPC17QL6EhZJTUc"
|
||||||
"IWRBzwmtcVPC17QL6EhZJTUcZ0mTz7l0R0DocLmFwHEXFEEr+q4WaJjt"
|
"Z0mTz7l0R0DocLmFwHEXFEEr+q4WaJjt1ejOOxVM3tiT7D8Y"
|
||||||
"1ejOOxVM3tiT7D8YpRZnnGNPfvEhq1yVMUoi8yv9pFmMmXicNBhm6zDK"
|
"pRZnnGNPfvEhq1yVMUoi8yv9pFmMmXicNBhm6zDKVjcWk0gf"
|
||||||
"VjcWk0gfbvaQcMnnOLrrE1VxAAzyNyPIXBI/H7AAHz2ECz7dgd2/4ocv"
|
"bvaQcMnnOLrrE1VxAAzyNyPIXBI/H7AAHz2ECz7dgd2/4ocv"
|
||||||
"3bmTRY3hhcUKtNuat2IOvSGgMBUGdWnLorQGFz8t0/bcYhE0Dve35U6H"
|
"3bmTRY3hhcUKtNuat2IOvSGgMBUGdWnLorQGFz8t0/bcYhE0"
|
||||||
"mtj78ydV/wmQWG0iq49NX6hk+VUmZtSZztlkbsaa7ajNjZ+Md9oZtlhX"
|
"Dve35U6Hmtj78ydV/wmQWG0iq49NX6hk+VUmZtSZztlkbsaa"
|
||||||
"Z5vJuhRXnHiCm7dRNO8Xo6HffEBH5A4smQ1T2Kda+1c18DZrY7+iQJRi"
|
"7ajNjZ+Md9oZtlhXZ5vJuhRXnHiCm7dRNO8Xo6HffEBH5A4s"
|
||||||
"fa6witPCw0tXkQ6nlCLqL2weJD1XMiTZLSM/XsZFGGSkKCKvKLEqQrI/"
|
"mQ1T2Kda+1c18DZrY7+iQJRifa6witPCw0tXkQ6nlCLqL2we"
|
||||||
"XFUq/TA6B4aLGFlmmhOO/vMJcht06O8qVU/xtd5Mv/MRFzYaSG568Z/m"
|
"JD1XMiTZLSM/XsZFGGSkKCKvKLEqQrI/XFUq/TA6B4aLGFlm"
|
||||||
"hk4vYLYdQYAA+pXRW9A=",
|
"mhOO/vMJcht06O8qVU/xtd5Mv/MRFzYaSG568Z/mhk4vYLYd"
|
||||||
"uuid": "4b32ddf7-7941-4c36-a854-a1f5ac45b318",
|
"QYAA+pXRW9A=",
|
||||||
"availability_zone": "nova",
|
"uuid": "4b32ddf7-7941-4c36-a854-a1f5ac45b318",
|
||||||
"hostname": "windows.novalocal",
|
"availability_zone": "nova",
|
||||||
"launch_index": 0,
|
"hostname": "windows.novalocal",
|
||||||
"public_keys": {"key": "ssh-rsa "
|
"launch_index": 0,
|
||||||
"AAAAB3NzaC1yc2EAAAADAQABAAABA"
|
"public_keys": {
|
||||||
"QDf7kQHq7zvBod3yIZs0tB/AOOZz5pab7qt/h"
|
"key":
|
||||||
"78VF7yi6qTsFdUnQxRue43R/75wa9EEyokgYR"
|
"ssh-rsa "
|
||||||
"LKIN+Jq2A5tXNMcK+rNOCzLJFtioAwEl+S6VL"
|
"AAAAB3NzaC1yc2EAAAADAQABAAABA"
|
||||||
"G9jfkbUv++7zoSMOsanNmEDvG0B79MpyECFCl"
|
"QDf7kQHq7zvBod3yIZs0tB/AOOZz5pab7qt/h"
|
||||||
"th2DsdE4MQypify35U5ri5Qi7E6PEYAsU65LF"
|
"78VF7yi6qTsFdUnQxRue43R/75wa9EEyokgYR"
|
||||||
"MG2boeCIB29BEooE6AgPr2DuJeJ+2uw+YScF9"
|
"LKIN+Jq2A5tXNMcK+rNOCzLJFtioAwEl+S6VL"
|
||||||
"FV3og4Wyz5zipPVh8YpVev6dlg0tRWUrCtZF9"
|
"G9jfkbUv++7zoSMOsanNmEDvG0B79MpyECFCl"
|
||||||
"IODpCTrT3vsPRG3xz7CppR+vGi/1gLXHtJCRj"
|
"th2DsdE4MQypify35U5ri5Qi7E6PEYAsU65LF"
|
||||||
"frHwkY6cXyhypNmkU99K/wMqSv30vsDwdnsQ1"
|
"MG2boeCIB29BEooE6AgPr2DuJeJ+2uw+YScF9"
|
||||||
"q3YhLarMHB Generated by Nova\n",
|
"FV3og4Wyz5zipPVh8YpVev6dlg0tRWUrCtZF9"
|
||||||
0: "windows"},
|
"IODpCTrT3vsPRG3xz7CppR+vGi/1gLXHtJCRj"
|
||||||
"network_config": {"content_path": "network",
|
"frHwkY6cXyhypNmkU99K/wMqSv30vsDwdnsQ1"
|
||||||
'debian_config': 'iface eth0 inet static'
|
"q3YhLarMHB Generated by Nova\n",
|
||||||
'address 10.11.12.13'
|
0: "windows"
|
||||||
'broadcast 0.0.0.0'
|
},
|
||||||
'netmask 255.255.255.255'
|
"network_config": {
|
||||||
'gateway 1.2.3.4'
|
"content_path": "network",
|
||||||
'dns-nameserver 8.8.8.8'}}
|
"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
|
# Copyright 2014 Cloudbase Solutions Srl
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import mock
|
|
||||||
|
import functools
|
||||||
import posixpath
|
import posixpath
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
from cloudbaseinit.metadata.services import base
|
from cloudbaseinit.metadata.services import base
|
||||||
from cloudbaseinit.metadata.services import baseopenstackservice
|
from cloudbaseinit.metadata.services import baseopenstackservice
|
||||||
|
from cloudbaseinit.tests.metadata import fake_json_response
|
||||||
from cloudbaseinit.utils import x509constants
|
from cloudbaseinit.utils import x509constants
|
||||||
from oslo.config import cfg
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
MODPATH = "cloudbaseinit.metadata.services.baseopenstackservice"
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseOpenStackService(unittest.TestCase):
|
||||||
|
|
||||||
class BaseOpenStackServiceTest(unittest.TestCase):
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
CONF.set_override('retry_count_interval', 0)
|
CONF.set_override("retry_count_interval", 0)
|
||||||
self._service = baseopenstackservice.BaseOpenStackService()
|
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")
|
".BaseOpenStackService._get_cache_data")
|
||||||
def test_get_content(self, mock_get_cache_data):
|
def test_get_content(self, mock_get_cache_data):
|
||||||
response = self._service.get_content('fake name')
|
response = self._service.get_content('fake name')
|
||||||
@ -39,7 +54,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
|||||||
mock_get_cache_data.assert_called_once_with(path)
|
mock_get_cache_data.assert_called_once_with(path)
|
||||||
self.assertEqual(mock_get_cache_data.return_value, response)
|
self.assertEqual(mock_get_cache_data.return_value, response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_cache_data")
|
".BaseOpenStackService._get_cache_data")
|
||||||
def test_get_user_data(self, mock_get_cache_data):
|
def test_get_user_data(self, mock_get_cache_data):
|
||||||
response = self._service.get_user_data()
|
response = self._service.get_user_data()
|
||||||
@ -47,7 +62,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
|||||||
mock_get_cache_data.assert_called_once_with(path)
|
mock_get_cache_data.assert_called_once_with(path)
|
||||||
self.assertEqual(mock_get_cache_data.return_value, response)
|
self.assertEqual(mock_get_cache_data.return_value, response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_cache_data")
|
".BaseOpenStackService._get_cache_data")
|
||||||
def test_get_meta_data(self, mock_get_cache_data):
|
def test_get_meta_data(self, mock_get_cache_data):
|
||||||
mock_get_cache_data.return_value = b'{"fake": "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)
|
mock_get_cache_data.assert_called_with(path)
|
||||||
self.assertEqual({"fake": "data"}, response)
|
self.assertEqual({"fake": "data"}, response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_meta_data")
|
".BaseOpenStackService._get_meta_data")
|
||||||
def test_get_instance_id(self, mock_get_meta_data):
|
def test_get_instance_id(self, mock_get_meta_data):
|
||||||
response = self._service.get_instance_id()
|
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,
|
self.assertEqual(mock_get_meta_data.return_value.get.return_value,
|
||||||
response)
|
response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_meta_data")
|
".BaseOpenStackService._get_meta_data")
|
||||||
def test_get_host_name(self, mock_get_meta_data):
|
def test_get_host_name(self, mock_get_meta_data):
|
||||||
response = self._service.get_host_name()
|
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,
|
self.assertEqual(mock_get_meta_data.return_value.get.return_value,
|
||||||
response)
|
response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_meta_data")
|
".BaseOpenStackService._get_meta_data")
|
||||||
def test_get_public_keys(self, mock_get_meta_data):
|
def test_get_public_keys(self, mock_get_meta_data):
|
||||||
response = self._service.get_public_keys()
|
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')
|
mock_get_meta_data().get.assert_called_once_with('public_keys')
|
||||||
self.assertEqual(mock_get_meta_data().get().values(), response)
|
self.assertEqual(mock_get_meta_data().get().values(), response)
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".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"
|
|
||||||
".BaseOpenStackService._get_meta_data")
|
".BaseOpenStackService._get_meta_data")
|
||||||
def _test_get_admin_password(self, mock_get_meta_data, meta_data):
|
def _test_get_admin_password(self, mock_get_meta_data, meta_data):
|
||||||
mock_get_meta_data.return_value = 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'):
|
elif meta_data and 'admin_pass' in meta_data.get('meta'):
|
||||||
self.assertEqual(meta_data.get('meta')['admin_pass'], response)
|
self.assertEqual(meta_data.get('meta')['admin_pass'], response)
|
||||||
else:
|
else:
|
||||||
self.assertEqual(None, response)
|
self.assertIsNone(response)
|
||||||
|
|
||||||
def test_get_admin_pass(self):
|
def test_get_admin_pass(self):
|
||||||
self._test_get_admin_password(meta_data={'admin_pass': 'fake pass'})
|
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):
|
def test_get_admin_pass_no_pass(self):
|
||||||
self._test_get_admin_password(meta_data={})
|
self._test_get_admin_password(meta_data={})
|
||||||
|
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService._get_meta_data")
|
".BaseOpenStackService._get_meta_data")
|
||||||
@mock.patch("cloudbaseinit.metadata.services.baseopenstackservice"
|
@mock.patch(MODPATH +
|
||||||
".BaseOpenStackService.get_user_data")
|
".BaseOpenStackService.get_user_data")
|
||||||
def _test_get_client_auth_certs(self, mock_get_user_data,
|
def _test_get_client_auth_certs(self, mock_get_user_data,
|
||||||
mock_get_meta_data, meta_data,
|
mock_get_meta_data, meta_data,
|
||||||
@ -132,7 +139,7 @@ class BaseOpenStackServiceTest(unittest.TestCase):
|
|||||||
mock_get_user_data.assert_called_once_with()
|
mock_get_user_data.assert_called_once_with()
|
||||||
self.assertEqual([ret_value], response)
|
self.assertEqual([ret_value], response)
|
||||||
elif ret_value is base.NotExistingMetadataException:
|
elif ret_value is base.NotExistingMetadataException:
|
||||||
self.assertEqual(None, response)
|
self.assertIsNone(response)
|
||||||
|
|
||||||
def test_get_client_auth_certs(self):
|
def test_get_client_auth_certs(self):
|
||||||
self._test_get_client_auth_certs(
|
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):
|
def test_get_client_auth_certs_no_cert_data_exception(self):
|
||||||
self._test_get_client_auth_certs(
|
self._test_get_client_auth_certs(
|
||||||
meta_data={}, ret_value=base.NotExistingMetadataException)
|
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(
|
details = base.NetworkDetails(
|
||||||
|
opennebulaservice.IF_FORMAT.format(iid=iid),
|
||||||
MAC,
|
MAC,
|
||||||
ADDRESS,
|
ADDRESS,
|
||||||
NETMASK,
|
NETMASK,
|
||||||
@ -302,8 +303,9 @@ class TestLoadedOpenNebulaService(_TestOpenNebulaService):
|
|||||||
|
|
||||||
def test_multiple_nics(self):
|
def test_multiple_nics(self):
|
||||||
self.load_context(context=CONTEXT2)
|
self.load_context(context=CONTEXT2)
|
||||||
details = _get_nic_details()
|
nic1 = _get_nic_details(iid=0)
|
||||||
network_details = [details] * 2
|
nic2 = _get_nic_details(iid=1)
|
||||||
|
network_details = [nic1, nic2]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
network_details,
|
network_details,
|
||||||
self._service.get_network_details()
|
self._service.get_network_details()
|
||||||
|
@ -13,163 +13,226 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
import re
|
import functools
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo.config import cfg
|
|
||||||
|
|
||||||
from cloudbaseinit import exception
|
from cloudbaseinit import exception
|
||||||
from cloudbaseinit.metadata.services import base as service_base
|
from cloudbaseinit.metadata.services import base as service_base
|
||||||
from cloudbaseinit.plugins import base as plugin_base
|
from cloudbaseinit.plugins import base as plugin_base
|
||||||
from cloudbaseinit.plugins.windows import networkconfig
|
from cloudbaseinit.plugins.windows import networkconfig
|
||||||
from cloudbaseinit.tests.metadata import fake_json_response
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class TestNetworkConfigPlugin(unittest.TestCase):
|
class TestNetworkConfigPlugin(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self._network_plugin = networkconfig.NetworkConfigPlugin()
|
self._setUp()
|
||||||
self.fake_data = fake_json_response.get_fake_metadata_json(
|
|
||||||
'2013-04-04')
|
|
||||||
|
|
||||||
@mock.patch('cloudbaseinit.osutils.factory.get_os_utils')
|
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
|
||||||
def _test_execute(self, mock_get_os_utils,
|
def _test_execute(self, mock_get_os_utils,
|
||||||
search_result=mock.MagicMock(),
|
network_adapters=None,
|
||||||
no_adapter_name=False, no_adapters=False,
|
network_details=None,
|
||||||
using_content=0, details_list=None,
|
invalid_details=False,
|
||||||
missing_content_path=False):
|
missed_adapters=[],
|
||||||
fake_adapter = ("fake_name_0", "fake_mac_0")
|
extra_network_details=[]):
|
||||||
|
# prepare mock environment
|
||||||
mock_service = mock.MagicMock()
|
mock_service = mock.MagicMock()
|
||||||
|
mock_shared_data = mock.Mock()
|
||||||
mock_osutils = mock.MagicMock()
|
mock_osutils = mock.MagicMock()
|
||||||
mock_ndetails = mock.Mock()
|
mock_service.get_network_details.return_value = network_details
|
||||||
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_get_os_utils.return_value = mock_osutils
|
mock_get_os_utils.return_value = mock_osutils
|
||||||
mock_osutils.set_static_network_config.return_value = False
|
mock_osutils.get_network_adapters.return_value = network_adapters
|
||||||
# service method setup
|
mock_osutils.set_static_network_config.return_value = True
|
||||||
methods = ["get_network_config", "get_content", "get_network_details"]
|
network_execute = functools.partial(
|
||||||
for method in methods:
|
self._network_plugin.execute,
|
||||||
mock_method = getattr(mock_service, method)
|
mock_service, mock_shared_data
|
||||||
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]
|
|
||||||
# actual tests
|
# actual tests
|
||||||
if search_result is None and using_content == 1:
|
if not network_details:
|
||||||
self.assertRaises(exception.CloudbaseInitException,
|
ret = network_execute()
|
||||||
self._network_plugin.execute,
|
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, False), ret)
|
||||||
mock_service, fake_shared_data)
|
|
||||||
return
|
return
|
||||||
if no_adapters:
|
if invalid_details:
|
||||||
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:
|
|
||||||
with self.assertRaises(exception.CloudbaseInitException):
|
with self.assertRaises(exception.CloudbaseInitException):
|
||||||
self._network_plugin.execute(mock_service,
|
network_execute()
|
||||||
fake_shared_data)
|
return
|
||||||
mock_service.get_network_details.reset_mock()
|
if not network_adapters:
|
||||||
mock_ndetails = service_base.NetworkDetails(*details_list)
|
with self.assertRaises(exception.CloudbaseInitException):
|
||||||
mock_service.get_network_details.return_value = [mock_ndetails]
|
network_execute()
|
||||||
response = self._network_plugin.execute(mock_service,
|
return
|
||||||
fake_shared_data)
|
# good to go for the configuration process
|
||||||
mock_service.get_network_details.assert_called_once_with()
|
ret = network_execute()
|
||||||
mac = mock_ndetails.mac
|
calls = []
|
||||||
(
|
for adapter in set(network_adapters) - set(missed_adapters):
|
||||||
address,
|
nics = [nic for nic in (network_details +
|
||||||
netmask,
|
extra_network_details)
|
||||||
broadcast,
|
if nic.mac == adapter[1]]
|
||||||
gateway,
|
self.assertTrue(nics) # missed_adapters should do the job
|
||||||
dnsnameservers
|
nic = nics[0]
|
||||||
) = map(lambda attr: getattr(mock_ndetails, attr), attrs)
|
call = mock.call(
|
||||||
if using_content in (1, 2) and not missing_content_path:
|
nic.mac,
|
||||||
mock_osutils.set_static_network_config.assert_called_once_with(
|
nic.address,
|
||||||
mac,
|
nic.netmask,
|
||||||
address,
|
nic.broadcast,
|
||||||
netmask,
|
nic.gateway,
|
||||||
broadcast,
|
nic.dnsnameservers
|
||||||
gateway,
|
|
||||||
dnsnameservers
|
|
||||||
)
|
)
|
||||||
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, False),
|
calls.append(call)
|
||||||
response)
|
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):
|
def _setUp(self, same_names=True):
|
||||||
self._test_execute(using_content=1)
|
# 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):
|
def test_execute_no_network_details(self):
|
||||||
self._test_execute(using_content=1, missing_content_path=True)
|
self._network_details[:] = []
|
||||||
|
self._partial_test_execute()
|
||||||
|
|
||||||
def test_execute_no_debian(self):
|
def test_execute_no_network_adapters(self):
|
||||||
self._test_execute(search_result=None, using_content=1)
|
self._network_adapters[:] = []
|
||||||
|
self._partial_test_execute()
|
||||||
|
|
||||||
def test_execute_no_adapter_name(self):
|
def test_execute_invalid_network_details(self):
|
||||||
self._test_execute(no_adapter_name=True, using_content=1)
|
self._network_details.append([None] * 6)
|
||||||
|
self._partial_test_execute(invalid_details=True)
|
||||||
|
|
||||||
def test_execute_no_adapter_name_or_adapters(self):
|
def test_execute_single(self):
|
||||||
self._test_execute(no_adapter_name=True, no_adapters=True,
|
for _ in range(self._count - 1):
|
||||||
using_content=1)
|
self._network_adapters.pop()
|
||||||
|
self._network_details.pop()
|
||||||
|
self._partial_test_execute()
|
||||||
|
|
||||||
def test_execute_network_details(self):
|
def test_execute_multiple(self):
|
||||||
self._test_execute(using_content=2)
|
self._partial_test_execute()
|
||||||
|
|
||||||
def test_execute_no_config_or_details(self):
|
def test_execute_missing_one(self):
|
||||||
self._test_execute(using_content=0)
|
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