Adds Windows support
Windows Server 2016 includes support for containers and Docker. This commit adds portability support for running nova-docker on Windows. Co-Authored-By: Alin Balutoiu <abalutoiu@cloudbasesolutions.com> Change-Id: Iabd667dd6ed0b1ecb277f527bff1c6a1ce51ecea Implements: blueprint windows-docker-support
This commit is contained in:
parent
8cdafd4e81
commit
54b3791a25
@ -13,48 +13,60 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import posix
|
||||
|
||||
import mock
|
||||
import multiprocessing
|
||||
|
||||
from nova import test
|
||||
from novadocker.virt.docker import hostinfo
|
||||
import psutil
|
||||
|
||||
|
||||
class HostInfoTestCase(test.NoDBTestCase):
|
||||
|
||||
_FAKE_DISK_INFO = {'total_size': 100000,
|
||||
'free_size': 50000,
|
||||
'used_size': 50000}
|
||||
|
||||
def setUp(self):
|
||||
super(HostInfoTestCase, self).setUp()
|
||||
self.stubs.Set(hostinfo, 'statvfs', self.statvfs)
|
||||
|
||||
def statvfs(self):
|
||||
seq = (4096, 4096, 10047582, 7332259, 6820195,
|
||||
2564096, 2271310, 2271310, 1024, 255)
|
||||
return posix.statvfs_result(sequence=seq)
|
||||
diskinfo = psutil.namedtuple('usage', ('total', 'free', 'used'))
|
||||
return diskinfo(self._FAKE_DISK_INFO['total_size'],
|
||||
self._FAKE_DISK_INFO['free_size'],
|
||||
self._FAKE_DISK_INFO['used_size'])
|
||||
|
||||
def test_get_disk_usage(self):
|
||||
disk_usage = hostinfo.get_disk_usage()
|
||||
self.assertEqual(disk_usage['total'], 41154895872)
|
||||
self.assertEqual(disk_usage['available'], 27935518720)
|
||||
self.assertEqual(disk_usage['used'], 11121963008)
|
||||
self.assertEqual(disk_usage['total'],
|
||||
self._FAKE_DISK_INFO['total_size'])
|
||||
self.assertEqual(disk_usage['available'],
|
||||
self._FAKE_DISK_INFO['free_size'])
|
||||
self.assertEqual(disk_usage['used'],
|
||||
self._FAKE_DISK_INFO['used_size'])
|
||||
|
||||
@mock.patch.object(multiprocessing, 'cpu_count')
|
||||
def test_get_total_vcpus(self, mock_cpu_count):
|
||||
mock_cpu_count.return_value = 1
|
||||
|
||||
cpu_count = hostinfo.get_total_vcpus()
|
||||
|
||||
self.assertEqual(mock_cpu_count.return_value, cpu_count)
|
||||
|
||||
def test_get_memory_usage(self):
|
||||
meminfo_str = """MemTotal: 1018784 kB
|
||||
MemFree: 220060 kB
|
||||
Buffers: 21640 kB
|
||||
Cached: 63364 kB
|
||||
SwapCached: 0 kB
|
||||
Active: 13988 kB
|
||||
Inactive: 50616 kB
|
||||
"""
|
||||
with mock.patch('__builtin__.open',
|
||||
mock.mock_open(read_data=meminfo_str),
|
||||
create=True) as m:
|
||||
fake_total_memory = 4096
|
||||
fake_used_memory = 2048
|
||||
|
||||
with mock.patch.object(psutil,
|
||||
'virtual_memory') as mock_virtual_memory:
|
||||
mock_virtual_memory.return_value.total = fake_total_memory
|
||||
mock_virtual_memory.return_value.used = fake_used_memory
|
||||
|
||||
usage = hostinfo.get_memory_usage()
|
||||
m.assert_called_once_with('/proc/meminfo')
|
||||
self.assertEqual(usage['total'], 1043234816)
|
||||
self.assertEqual(usage['used'], 730849280)
|
||||
|
||||
self.assertEqual(fake_total_memory, usage['total'])
|
||||
self.assertEqual(fake_used_memory, usage['used'])
|
||||
|
||||
@mock.patch('novadocker.virt.docker.hostinfo.get_mounts')
|
||||
def test_find_cgroup_devices_path_centos(self, mock):
|
||||
|
@ -29,24 +29,34 @@ import mock
|
||||
|
||||
|
||||
class NetworkTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(network, 'os')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_teardown_delete_network(self, utils_mock):
|
||||
def test_teardown_delete_network(self, utils_mock, mock_os):
|
||||
id = "second-id"
|
||||
utils_mock.return_value = ("first-id\nsecond-id\nthird-id\n", None)
|
||||
network.teardown_network(id)
|
||||
utils_mock.assert_called_with('ip', 'netns', 'delete', id,
|
||||
run_as_root=True)
|
||||
|
||||
@mock.patch.object(network, 'os')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_teardown_network_not_in_list(self, utils_mock):
|
||||
def test_teardown_network_not_in_list(self, utils_mock, mock_os):
|
||||
utils_mock.return_value = ("first-id\nsecond-id\nthird-id\n", None)
|
||||
network.teardown_network("not-in-list")
|
||||
utils_mock.assert_called_with('ip', '-o', 'netns', 'list')
|
||||
|
||||
@mock.patch.object(network, 'os')
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_teardown_network_hyperv(self, utils_mock, mock_os):
|
||||
mock_os.name = 'nt'
|
||||
network.teardown_network("fake_id")
|
||||
self.assertFalse(utils_mock.execute.called)
|
||||
|
||||
@mock.patch.object(network, 'os')
|
||||
@mock.patch.object(network, 'LOG')
|
||||
@mock.patch.object(utils, 'execute',
|
||||
side_effect=processutils.ProcessExecutionError)
|
||||
def test_teardown_network_fails(self, utils_mock, log_mock):
|
||||
def test_teardown_network_fails(self, utils_mock, log_mock, mock_os):
|
||||
# Call fails but method should not fail.
|
||||
# Error will be caught and logged.
|
||||
utils_mock.return_value = ("first-id\nsecond-id\nthird-id\n", None)
|
||||
|
@ -316,6 +316,26 @@ class DockerGenericVIFDriverTestCase(test.TestCase):
|
||||
'project_id': tenant_id}, network_info)
|
||||
ex.assert_has_calls(calls)
|
||||
|
||||
def test_plug_vifs_windows(self):
|
||||
network_info = [{'type': 'hyperv'}]
|
||||
fake_instance = mock.sentinel.instance
|
||||
with mock.patch.object(vifs.DockerGenericVIFDriver,
|
||||
'plug_windows') as mock_plug_windows:
|
||||
driver = docker_driver.DockerDriver(object)
|
||||
driver.plug_vifs(fake_instance, network_info)
|
||||
mock_plug_windows.assert_called_once_with(
|
||||
fake_instance, {'type': 'hyperv'})
|
||||
|
||||
def test_unplug_vifs_windows(self):
|
||||
network_info = [{'type': 'hyperv'}]
|
||||
fake_instance = mock.sentinel.instance
|
||||
with mock.patch.object(vifs.DockerGenericVIFDriver,
|
||||
'unplug_windows') as mock_unplug_windows:
|
||||
driver = docker_driver.DockerDriver(object)
|
||||
driver.unplug_vifs(fake_instance, network_info)
|
||||
mock_unplug_windows.assert_called_once_with(
|
||||
fake_instance, {'type': 'hyperv'})
|
||||
|
||||
def test_unplug_vifs_ovs_hybrid(self):
|
||||
iface_id = '920be2f4-2b98-411e-890a-69bcabb2a5a0'
|
||||
calls = [
|
||||
@ -456,3 +476,13 @@ class DockerGenericVIFDriverTestCase(test.TestCase):
|
||||
driver = docker_driver.DockerDriver(object)
|
||||
driver._attach_vifs({'uuid': 'fake_uuid'}, network_info)
|
||||
ex.assert_has_calls(calls)
|
||||
|
||||
@mock.patch.object(docker_driver, 'os')
|
||||
def test_attach_vifs_hyperv(self, mock_os):
|
||||
mock_os.name = 'nt'
|
||||
with mock.patch.object(docker_driver.DockerDriver,
|
||||
'_get_container_id') as mock_get_container_id:
|
||||
driver = docker_driver.DockerDriver(object)
|
||||
driver._attach_vifs(mock.sentinel.instance,
|
||||
mock.sentinel.network_info)
|
||||
self.assertFalse(mock_get_container_id.called)
|
||||
|
@ -20,9 +20,35 @@ from novadocker.virt import hostutils
|
||||
|
||||
|
||||
class HostUtilsTestCase(test.NoDBTestCase):
|
||||
def test_sys_uptime(self):
|
||||
expect_uptime = "this is my uptime"
|
||||
with mock.patch('nova.utils.execute',
|
||||
return_value=(expect_uptime, None)):
|
||||
def _test_sys_uptime(self, is_nt_os=False):
|
||||
expect_uptime = ("fake_time up 0:00:00, 0 users, "
|
||||
"load average: 0, 0, 0")
|
||||
fake_tick_count = 0
|
||||
fake_time = 'fake_time'
|
||||
|
||||
with mock.patch.multiple(hostutils, os=mock.DEFAULT, time=mock.DEFAULT,
|
||||
ctypes=mock.DEFAULT, utils=mock.DEFAULT,
|
||||
create=True) as lib_mocks:
|
||||
|
||||
lib_mocks['os'].name = 'nt' if is_nt_os else ''
|
||||
lib_mocks['time'].strftime.return_value = fake_time
|
||||
lib_mocks['utils'].execute.return_value = (expect_uptime, None)
|
||||
tick_count = lib_mocks['ctypes'].windll.kernel32.GetTickCount64
|
||||
tick_count.return_value = fake_tick_count
|
||||
|
||||
uptime = hostutils.sys_uptime()
|
||||
self.assertEqual(expect_uptime, uptime)
|
||||
|
||||
if is_nt_os:
|
||||
tick_count.assert_called_once_with()
|
||||
lib_mocks['time'].strftime.assert_called_once_with("%H:%M:%S")
|
||||
else:
|
||||
lib_mocks['utils'].execute.assert_called_once_with(
|
||||
'env', 'LANG=C', 'uptime')
|
||||
|
||||
self.assertEqual(expect_uptime, uptime)
|
||||
|
||||
def test_sys_uptime(self):
|
||||
self._test_sys_uptime()
|
||||
|
||||
def test_nt_sys_uptime(self):
|
||||
self._test_sys_uptime(is_nt_os=True)
|
||||
|
@ -92,7 +92,7 @@ class DockerHTTPClient(client.Client):
|
||||
return res.status_code == 204
|
||||
|
||||
def load_repository_file(self, name, path):
|
||||
with open(path) as fh:
|
||||
with open(path, 'rb') as fh:
|
||||
self.load_image(fh)
|
||||
|
||||
def get_container_logs(self, container_id):
|
||||
|
@ -224,6 +224,10 @@ class DockerDriver(driver.ComputeDriver):
|
||||
"""Plug VIFs into container."""
|
||||
if not network_info:
|
||||
return
|
||||
|
||||
if os.name == 'nt':
|
||||
return
|
||||
|
||||
container_id = self._get_container_id(instance)
|
||||
if not container_id:
|
||||
return
|
||||
|
@ -13,9 +13,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import multiprocessing
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
import psutil
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -24,7 +26,7 @@ def statvfs():
|
||||
docker_path = CONF.docker.root_directory
|
||||
if not os.path.exists(docker_path):
|
||||
docker_path = '/'
|
||||
return os.statvfs(docker_path)
|
||||
return psutil.disk_usage(docker_path)
|
||||
|
||||
|
||||
def get_disk_usage():
|
||||
@ -32,21 +34,14 @@ def get_disk_usage():
|
||||
# hardcoded in Docker so it's not configurable yet.
|
||||
st = statvfs()
|
||||
return {
|
||||
'total': st.f_blocks * st.f_frsize,
|
||||
'available': st.f_bavail * st.f_frsize,
|
||||
'used': (st.f_blocks - st.f_bfree) * st.f_frsize
|
||||
'total': st.total,
|
||||
'available': st.free,
|
||||
'used': st.used
|
||||
}
|
||||
|
||||
|
||||
def get_total_vcpus():
|
||||
total_vcpus = 0
|
||||
|
||||
with open('/proc/cpuinfo') as f:
|
||||
for ln in f.readlines():
|
||||
if ln.startswith('processor'):
|
||||
total_vcpus += 1
|
||||
|
||||
return total_vcpus
|
||||
return multiprocessing.cpu_count()
|
||||
|
||||
|
||||
def get_vcpus_used(containers):
|
||||
@ -60,19 +55,10 @@ def get_vcpus_used(containers):
|
||||
|
||||
|
||||
def get_memory_usage():
|
||||
with open('/proc/meminfo') as f:
|
||||
m = f.read().split()
|
||||
idx1 = m.index('MemTotal:')
|
||||
idx2 = m.index('MemFree:')
|
||||
idx3 = m.index('Buffers:')
|
||||
idx4 = m.index('Cached:')
|
||||
|
||||
total = int(m[idx1 + 1])
|
||||
avail = int(m[idx2 + 1]) + int(m[idx3 + 1]) + int(m[idx4 + 1])
|
||||
|
||||
vmem = psutil.virtual_memory()
|
||||
return {
|
||||
'total': total * 1024,
|
||||
'used': (total - avail) * 1024
|
||||
'total': vmem.total,
|
||||
'used': vmem.used
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log
|
||||
|
||||
@ -25,6 +27,9 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def teardown_network(container_id):
|
||||
if os.name == 'nt':
|
||||
return
|
||||
|
||||
try:
|
||||
output, err = utils.execute('ip', '-o', 'netns', 'list')
|
||||
for line in output.split('\n'):
|
||||
|
@ -66,10 +66,15 @@ class DockerGenericVIFDriver(object):
|
||||
self.plug_midonet(instance, vif)
|
||||
elif vif_type == network_model.VIF_TYPE_IOVISOR:
|
||||
self.plug_iovisor(instance, vif)
|
||||
elif vif_type == 'hyperv':
|
||||
self.plug_windows(instance, vif)
|
||||
else:
|
||||
raise exception.NovaException(
|
||||
_("Unexpected vif_type=%s") % vif_type)
|
||||
|
||||
def plug_windows(self, instance, vif):
|
||||
pass
|
||||
|
||||
def plug_iovisor(self, instance, vif):
|
||||
"""Plug docker vif into IOvisor
|
||||
|
||||
@ -333,10 +338,15 @@ class DockerGenericVIFDriver(object):
|
||||
self.unplug_midonet(instance, vif)
|
||||
elif vif_type == network_model.VIF_TYPE_IOVISOR:
|
||||
self.unplug_iovisor(instance, vif)
|
||||
elif vif_type == 'hyperv':
|
||||
self.unplug_windows(instance, vif)
|
||||
else:
|
||||
raise exception.NovaException(
|
||||
_("Unexpected vif_type=%s") % vif_type)
|
||||
|
||||
def unplug_windows(self, instance, vif):
|
||||
pass
|
||||
|
||||
def unplug_iovisor(self, instance, vif):
|
||||
"""Unplug vif from IOvisor
|
||||
|
||||
|
@ -14,10 +14,22 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import ctypes
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
|
||||
from nova import utils
|
||||
|
||||
|
||||
def sys_uptime():
|
||||
"""Returns the result of calling "uptime"."""
|
||||
out, err = utils.execute('env', 'LANG=C', 'uptime')
|
||||
return out
|
||||
"""Returns the host uptime."""
|
||||
|
||||
if os.name == 'nt':
|
||||
tick_count64 = ctypes.windll.kernel32.GetTickCount64()
|
||||
return ("%s up %s, 0 users, load average: 0, 0, 0" %
|
||||
(str(time.strftime("%H:%M:%S")),
|
||||
str(datetime.timedelta(milliseconds=long(tick_count64)))))
|
||||
else:
|
||||
out, err = utils.execute('env', 'LANG=C', 'uptime')
|
||||
return out
|
||||
|
Loading…
Reference in New Issue
Block a user