diff --git a/nova/tests/fixtures/libvirt.py b/nova/tests/fixtures/libvirt.py
index 648f1b01be..a1e9d31203 100644
--- a/nova/tests/fixtures/libvirt.py
+++ b/nova/tests/fixtures/libvirt.py
@@ -914,6 +914,25 @@ def _parse_vcpu_info(element):
return vcpu_info
+def _parse_filesystem_info(element):
+ filesystem_info = {}
+ filesystem_info['type'] = element.get('type', 'mount')
+
+ driver = element.find('./driver')
+ if driver is not None:
+ filesystem_info['driver_type'] = driver.get('type')
+
+ source = element.find('./source')
+ if source is not None:
+ filesystem_info['source'] = source.get('dir')
+
+ target = element.find('./target')
+ if target is not None:
+ filesystem_info['target'] = target.get('dir')
+
+ return filesystem_info
+
+
def _parse_nic_info(element):
nic_info = {}
nic_info['type'] = element.get('type', 'bridge')
@@ -1177,6 +1196,13 @@ class Domain(object):
disks_info += [_parse_disk_info(disk)]
devices['disks'] = disks_info
+ # Manage shares
+ filesystems_info = []
+ filesystems = device_nodes.findall('./filesystem')
+ for filesystem in filesystems:
+ filesystems_info += [_parse_filesystem_info(filesystem)]
+ devices['filesystem'] = filesystems_info
+
nics_info = []
nics = device_nodes.findall('./interface')
for nic in nics:
@@ -1458,6 +1484,13 @@ class Domain(object):
"""
disks += strformat % dict(source_attr=source_attr, **disk)
+ filesystems = ''
+ for filesystem in self._def['devices']['filesystem']:
+ filesystems += '''
+
+
+
+ ''' % dict(source_attr=source_attr, **filesystem)
nics = ''
for func, nic in enumerate(self._def['devices']['nics']):
if func > 7:
@@ -1590,6 +1623,7 @@ class Domain(object):
/usr/bin/kvm
%(disks)s
+ %(filesystems)s
@@ -1624,6 +1658,7 @@ class Domain(object):
'arch': self._def['os']['arch'],
'loader': loader,
'disks': disks,
+ 'filesystems': filesystems,
'nics': nics,
'hostdevs': hostdevs,
'vpmems': vpmems,
diff --git a/nova/tests/functional/libvirt/test_server_shares.py b/nova/tests/functional/libvirt/test_server_shares.py
new file mode 100644
index 0000000000..79feb50c38
--- /dev/null
+++ b/nova/tests/functional/libvirt/test_server_shares.py
@@ -0,0 +1,175 @@
+# 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 fixtures
+from lxml import etree
+from requests import request
+
+from nova import context as nova_context
+from nova.objects import instance
+from nova.tests import fixtures as nova_fixtures
+from nova.tests.functional.libvirt import base
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_serialization import jsonutils
+
+from unittest import mock
+
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+
+class ServerSharesTestBase(base.ServersTestBase):
+ api_major_version = 'v2.1'
+ microversion = 'latest'
+ ADMIN_API = True
+ FAKE_LIBVIRT_VERSION = 7000000
+ FAKE_QEMU_VERSION = 5002000
+
+ def setUp(self):
+ super(ServerSharesTestBase, self).setUp()
+
+ self.context = nova_context.get_admin_context()
+ self.manila_fixture = self.useFixture(nova_fixtures.ManilaFixture())
+ self.flags(ram_allocation_ratio=1.0)
+ self.flags(file_backed_memory=8192, group='libvirt')
+ self.compute = self.start_compute(
+ 'host1',
+ libvirt_version=self.FAKE_LIBVIRT_VERSION,
+ qemu_version=self.FAKE_QEMU_VERSION
+ )
+
+ self.api_fixture = self.useFixture(nova_fixtures.OSMetadataServer())
+ self.md_url = self.api_fixture.md_url
+
+ self.host = self.computes[self.compute].driver._host
+
+ def _get_xml(self, server):
+ self.instance = instance.Instance.get_by_uuid(
+ self.context, server['id'])
+ guest = self.host.get_guest(self.instance)
+ xml = guest.get_xml_desc()
+ return xml
+
+ def _get_filesystem_tag(self, xml, tag):
+ tags = []
+ tree = etree.fromstring(xml)
+ device_nodes = tree.find('./devices')
+ filesystems = device_nodes.findall('./filesystem')
+ for filesystem in filesystems:
+ target = filesystem.find('./target')
+ tags.append(target.get('dir'))
+ return tags
+
+ def _assert_filesystem_tag(self, xml, tag):
+ tags = self._get_filesystem_tag(xml, tag)
+ self.assertIn(tag, tags)
+
+ def _assert_filesystem_tag_not_present(self, xml, tag):
+ tags = self._get_filesystem_tag(xml, tag)
+ self.assertNotIn(tag, tags)
+
+ def _get_metadata_url(self, server):
+ # make sure that the metadata service returns information about the
+ # server we created above
+
+ def fake_get_fixed_ip_by_address(self, ctxt, address):
+ return {'instance_uuid': server['id']}
+
+ self.useFixture(
+ fixtures.MonkeyPatch(
+ 'nova.network.neutron.API.get_fixed_ip_by_address',
+ fake_get_fixed_ip_by_address))
+ url = '%sopenstack/latest/meta_data.json' % self.md_url
+ return url
+
+ def _assert_share_in_metadata(self, metatdata_url, share_id, tag):
+ device_share_and_tag = []
+ res = request('GET', metatdata_url, timeout=5)
+ self.assertEqual(200, res.status_code)
+ metadata = jsonutils.loads(res.text)
+ for device in metadata['devices']:
+ device_share_and_tag.append((device['share_id'], device['tag']))
+ self.assertIn((share_id, tag), device_share_and_tag)
+
+
+class ServerSharesTest(ServerSharesTestBase):
+
+ def test_server_share_metadata(self):
+ """Verify that share metadata are available"""
+ with mock.patch(
+ 'nova.virt.libvirt.volume.nfs.LibvirtNFSVolumeDriver.'
+ 'disconnect_volume'
+ ), mock.patch(
+ 'nova.virt.libvirt.volume.nfs.LibvirtNFSVolumeDriver.'
+ 'connect_volume'
+ ):
+ traits = self._get_provider_traits(
+ self.compute_rp_uuids[self.compute])
+ for trait in (
+ 'COMPUTE_STORAGE_VIRTIO_FS', 'COMPUTE_MEM_BACKING_FILE'):
+ self.assertIn(trait, traits)
+ server = self._create_server(networks='auto')
+ self._stop_server(server)
+
+ share_id = '4b021746-d0eb-4031-92aa-23c3bec182cd'
+ self._attach_share(server, share_id)
+ self._start_server(server)
+
+ # tag is the filesystem target directory.
+ # if post /server/{server_id}/share was called without a specific
+ # tag then the tag is the share id.
+ self._assert_filesystem_tag(self._get_xml(server), share_id)
+
+ self._assert_share_in_metadata(
+ self._get_metadata_url(server), share_id, share_id)
+ return (server, share_id)
+
+ def test_server_cephfs_share_metadata(self):
+ """Verify that cephfs share metadata are available"""
+ with mock.patch(
+ 'nova.virt.libvirt.volume.cephfs.LibvirtCEPHFSVolumeDriver.'
+ 'disconnect_volume'
+ ), mock.patch(
+ 'nova.virt.libvirt.volume.cephfs.LibvirtCEPHFSVolumeDriver.'
+ 'connect_volume'
+ ):
+ # update the mock to call the cephfs fake values
+ self.manila_fixture.mock_get.side_effect = (
+ self.manila_fixture.fake_get_cephfs
+ )
+ self.manila_fixture.mock_get_access.side_effect = (
+ self.manila_fixture.fake_get_access_cephfs
+ )
+
+ traits = self._get_provider_traits(
+ self.compute_rp_uuids[self.compute])
+ for trait in (
+ 'COMPUTE_STORAGE_VIRTIO_FS', 'COMPUTE_MEM_BACKING_FILE'):
+ self.assertIn(trait, traits)
+ server = self._create_server(networks='auto')
+ self._stop_server(server)
+
+ share_id = '4b021746-d0eb-4031-92aa-23c3bec182cd'
+ self._attach_share(server, share_id)
+ self._start_server(server)
+
+ # tag is the filesystem target directory.
+ # if post /server/{server_id}/share was called without a specific
+ # tag then the tag is the share id.
+ self._assert_filesystem_tag(self._get_xml(server), share_id)
+
+ self._assert_share_in_metadata(
+ self._get_metadata_url(server), share_id, share_id)
+ return (server, share_id)