Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConfigDrive support #24

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
.ropeproject
.tox
.venv
.testrepository
AUTHORS
Authors
build-stamp
Expand Down
2 changes: 1 addition & 1 deletion cloudbaseinit/metadata/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
cfg.ListOpt(
'metadata_services',
default=[
'cloudbaseinit.metadata.services.configdrive.ConfigDriveService',
'cloudbaseinit.metadata.services.httpservice.HttpService',
#'cloudbaseinit.metadata.services.configdrive.ConfigDriveService',
#'cloudbaseinit.metadata.services.ec2service.EC2Service',
#'cloudbaseinit.metadata.services.maasservice.MaaSHttpService',
#'cloudbaseinit.metadata.services.cloudstack.CloudStack',
Expand Down
10 changes: 8 additions & 2 deletions cloudbaseinit/metadata/services/osconfigdrive/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@

def get_config_drive_manager():
class_paths = {
'win32': 'cloudbaseinit.metadata.services.osconfigdrive.windows.'
'WindowsConfigDriveManager',
'freebsd10': (
'cloudbaseinit.metadata.services.osconfigdrive.freebsd.'
'FreeBSDConfigDriveManager'
),
'win32': (
'cloudbaseinit.metadata.services.osconfigdrive.windows.'
'WindowsConfigDriveManager'
),
}

class_path = class_paths.get(sys.platform)
Expand Down
136 changes: 136 additions & 0 deletions cloudbaseinit/metadata/services/osconfigdrive/freebsd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
from cloudbaseinit.metadata.services.osconfigdrive import base
from cloudbaseinit.openstack.common import log as logging
from oslo.config import cfg
import os
import contextlib
import shutil
import subprocess
import tempfile
import re

LOG = logging.getLogger(__name__)

class FreeBSDConfigDriveManager(base.BaseConfigDriveManager):

def get_config_drive_files(self, target_path, check_raw_hhd=True,
check_cdrom=True, check_vfat=True):
config_drive_found = False

if check_vfat:
LOG.debug('Looking for Config Drive in VFAT filesystems')
config_drive_found = self._get_conf_drive_from_vfat(target_path)

if not config_drive_found and check_raw_hhd:
LOG.debug('Looking for Config Drive in raw HDDs')
config_drive_found = self._get_conf_drive_from_raw_hdd(target_path)

if not config_drive_found and check_cdrom:
LOG.debug('Looking for Config Drive in cdrom drives')
config_drive_found = self._get_conf_drive_from_cdrom_drive(target_path)

return config_drive_found

def _get_conf_drive_from_vfat(self, target_path):
return False

def _get_conf_drive_from_raw_hdd(self, target_path):
return False

def _get_conf_drive_from_cdrom_drive(self, target_path):
cdrom = self._get_cdrom_device()
if not cdrom:
return False

with tempdir() as tmp:
umount = False
cdrom_mount_point = self._get_existing_cdrom_mount(cdrom)
if not cdrom_mount_point:
try:
mountcmd = ['mount', '-o', 'ro', '-t', 'cd9660', cdrom, tmp]
subprocess.check_call(mountcmd)
umount = tmp
cdrom_mount_point = tmp
except subprocess.CalledProcessError as exc:
LOG.debug('Failed mount of %s as %s: %s', cdrom, tmp, exc)
return False

with unmounter(umount):
shutil.copytree(cdrom_mount_point, target_path)
return True

return False

def _get_cdrom_device(self):
devices = self._get_devices()
cdrom_drives = ['/dev/%s' % d for d in devices if self._is_cdrom(d)]
if len(cdrom_drives):
return cdrom_drives[0]
return None

def _get_devices(self):
devices = []
cmd = 'sysctl -n kern.disks'
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
devices = output.split()
except subprocess.CalledProcessError:
pass

return devices

def _is_cdrom(self, device):
cmd = 'glabel status -s %s' % device
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
return output.startswith('iso9660/config-2')
except subprocess.CalledProcessError:
return False

def _get_existing_cdrom_mount(self, device):
existing_mounts = self._get_mounts()
mount = None
if device in existing_mounts:
mount = existing_mounts[os.path.realpath(device)]['mountpoint']
return mount

def _get_mounts(self):
mounts = {}
mountre = r'^(/dev/[\S]+) on (/.*) \((.+), .+, (.+)\)$'
cmd_output = subprocess.check_output('mount', stderr=subprocess.STDOUT, shell=True)
mount_info = cmd_output.split('\n')
for mount in mount_info:
try:
m = re.search(mountre, mount)
dev = m.group(1)
mp = m.group(2)
fstype = m.group(3)
opts = m.group(4)
except:
continue

mounts[dev] = {
'fstype': fstype,
'mountpoint': mp,
'opts': opts
}

return mounts


@contextlib.contextmanager
def tempdir(**kwargs):
tdir = tempfile.mkdtemp(**kwargs)
try:
yield tdir
finally:
shutil.rmtree(tdir)


@contextlib.contextmanager
def unmounter(umount):
try:
yield umount
finally:
if umount:
umount_cmd = ["umount", umount]
subprocess.check_call(umount_cmd)
13 changes: 9 additions & 4 deletions cloudbaseinit/osutils/freebsd.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def get_network_adapters(self):
"""
if_list = subprocess.check_output(['ifconfig', '-l']).split(' ')
# Filter out non-network interfaces
if_list = filter(lambda x: x.startswith(('pflog', 'lo', 'plip')), if_list)
if_list = filter(lambda x: not x.startswith(('pflog', 'lo', 'plip')), if_list)
return if_list

def set_static_network_config(self, adapter_name, address, netmask,
Expand All @@ -78,14 +78,19 @@ def set_static_network_config(self, adapter_name, address, netmask,

if_cmd = 'ifconfig ' + adapter_name + ' inet ' + address + ' netmask ' + netmask + ' broadcast ' + broadcast
route_cmd = 'route add default ' + gateway
resolv_conf = ['domain ' + dnsdomain]
resolv_conf_file = os.popen('resolvconf -a vtnet0', 'w', 1)
resolv_conf = []
if dnsdomain:
resolv_conf.append('domain ' + dnsdomain)

resolvconf_cmd = 'resolvconf -a %s' % adapter_name
resolv_conf_file = subprocess.Popen(
resolvconf_cmd, shell=True, bufsize=1, stdin=subprocess.PIPE).stdin
for i in dnsnameservers:
resolv_conf.append('nameserver ' + i)

subprocess.check_call(if_cmd, shell=True)
subprocess.check_call(route_cmd, shell=True)
self._add_comment(resolv_conf_file);
self._add_comment(resolv_conf_file)
for line in resolv_conf:
resolv_conf_file.write(line + '\n')
self._add_rc_conf({'ifconfig_' + adapter_name: 'inet ' + address + ' netmask ' + netmask + ' broadcast ' + broadcast,
Expand Down
2 changes: 1 addition & 1 deletion cloudbaseinit/plugins/common/execcmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class Python(BaseCommand):

class Bash(BaseCommand):
extension = '.sh'
command = 'bash'
command = 'sh'


class PowershellSysnative(BaseCommand):
Expand Down
3 changes: 2 additions & 1 deletion cloudbaseinit/plugins/common/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
cfg.ListOpt(
'plugins',
default=[
'cloudbaseinit.plugins.freebsd.networkconfig.NetworkConfigPlugin',
'cloudbaseinit.plugins.freebsd.sethostname.SetHostNamePlugin',
'cloudbaseinit.plugins.freebsd.scramblerootpassword.ScrambleRootPassword',
'cloudbaseinit.plugins.freebsd.createuser.CreateUserPlugin',
Expand All @@ -28,7 +29,7 @@
'cloudbaseinit.plugins.freebsd.sshpublickeys.'
'SetUserSSHPublicKeysPlugin',
#'cloudbaseinit.plugins.freebsd.extendvolumes.ExtendVolumesPlugin',
#'cloudbaseinit.plugins.freebsd.userdata.UserDataPlugin',
'cloudbaseinit.plugins.common.userdata.UserDataPlugin',
],
help='List of enabled plugin classes, '
'to executed in the provided order'),
Expand Down
35 changes: 8 additions & 27 deletions cloudbaseinit/plugins/freebsd/networkconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,39 +42,20 @@ def execute(self, service, shared_data):
if not network_details:
return (plugin_base.PLUGIN_EXECUTION_DONE, False)

if 'content_path' not in network_details:
return (base.PLUGIN_EXECUTION_DONE, False)

content_path = network_details['content_path']
content_name = content_path.rsplit('/', 1)[-1]
debian_network_conf = service.get_content(content_name)

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+(?P<dnsnameservers>[^\r\n]+)\s+',
debian_network_conf)
if not m:
raise exception.CloudbaseInitException(
"network_details format not recognized")

address = m.group('address')
netmask = m.group('netmask')
broadcast = m.group('broadcast')
gateway = m.group('gateway')
dnsnameservers = m.group('dnsnameservers').strip().split(' ')
address = network_details[0].address
netmask = network_details[0].netmask
broadcast = network_details[0].broadcast
gateway = network_details[0].gateway
dnsdomain = None
dnsnameservers = network_details[0].dnsnameservers

osutils = osutils_factory.get_os_utils()

network_adapter_name = CONF.network_adapter
if not network_adapter_name:
# Get the first available one
available_adapters = osutils.get_network_adapters()
LOG.debug('available adapters: %s', available_adapters)
if not len(available_adapters):
raise exception.CloudbaseInitException(
"No network adapter available")
Expand All @@ -84,6 +65,6 @@ def execute(self, service, shared_data):

reboot_required = osutils.set_static_network_config(
network_adapter_name, address, netmask, broadcast,
gateway, dnsnameservers)
gateway, dnsdomain, dnsnameservers)

return (base.PLUGIN_EXECUTION_DONE, reboot_required)
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,29 @@ def tearDown(self):
def _test_get_config_drive_manager(self, mock_load_class, platform):
sys.platform = platform

if platform is not "win32":
self.assertRaises(NotImplementedError,
factory.get_config_drive_manager)

else:
caught_notimplemented = False
try:
response = factory.get_config_drive_manager()
self.assertIsNotNone(response)
except NotImplementedError:
caught_notimplemented = True

if platform == 'win32':
mock_load_class.assert_called_once_with(
'cloudbaseinit.metadata.services.osconfigdrive.'
'windows.WindowsConfigDriveManager')

self.assertIsNotNone(response)
elif platform == 'freebsd10':
mock_load_class.assert_called_once_with(
'cloudbaseinit.metadata.services.osconfigdrive.'
'freebsd.FreeBSDConfigDriveManager')
else:
self.assertTrue(caught_notimplemented, 'NotImplementedError expected')

def test_get_config_drive_manager(self):
self._test_get_config_drive_manager(platform="win32")

def test_get_config_drive_manager_exception(self):
self._test_get_config_drive_manager(platform="other")

def test_get_config_drive_manager(self):
self._test_get_config_drive_manager(platform="freebsd10")
Loading