diff --git a/doc/ext/feature_matrix.py b/doc/ext/feature_matrix.py new file mode 100644 index 000000000000..8fb055f60891 --- /dev/null +++ b/doc/ext/feature_matrix.py @@ -0,0 +1,576 @@ +# 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. + +""" +This provides a sphinx extension able to render from an ini file (from the +doc/source/* directory) a feature matrix into the developer documentation. + +It is used via a single directive in the .rst file + + .. feature_matrix:: feature_classification.ini + +""" + +import re + +from six.moves import configparser + +from docutils import nodes +from docutils.parsers import rst + + +class Matrix(object): + """Matrix represents the entire feature matrix parsed from an ini file + + This includes: + * self.features is a list of MatrixFeature instances, the rows and cells + * self.targets is a dict of (MatrixTarget.key, MatrixTarget), the columns + """ + def __init__(self): + self.features = [] + self.targets = {} + + +class MatrixTarget(object): + + def __init__(self, key, title, driver, hypervisor=None, architecture=None, + link=None): + """MatrixTarget modes a target, a column in the matrix + + This is usually a specific CI system, or collection of related + deployment configurations. + + :param key: Unique identifier for the hypervisor driver + :param title: Human friendly name of the hypervisor + :param driver: Name of the Nova driver + :param hypervisor: (optional) Name of the hypervisor, if many + :param architecture: (optional) Name of the architecture, if many + :param link: (optional) URL to docs about this target + """ + self.key = key + self.title = title + self.driver = driver + self.hypervisor = hypervisor + self.architecture = architecture + self.link = link + + +class MatrixImplementation(object): + + STATUS_COMPLETE = "complete" + STATUS_PARTIAL = "partial" + STATUS_MISSING = "missing" + STATUS_UKNOWN = "unknown" + + STATUS_ALL = [STATUS_COMPLETE, STATUS_PARTIAL, STATUS_MISSING, + STATUS_UKNOWN] + + def __init__(self, status=STATUS_MISSING, notes=None, release=None): + """MatrixImplementation models a cell in the matrix + + This models the current state of a target for a specific feature + + :param status: One off complete, partial, missing, unknown. + See the RST docs for a definition of those. + :param notes: Arbitrary string describing any caveats of the + implementation. Mandatory if status is 'partial'. + :param release: Letter of the release entry was last updated. + i.e. m=mitaka, c=cactus. If not known it is None. + """ + self.status = status + self.notes = notes + self.release = release + + +class MatrixFeature(object): + MATURITY_INCOMPLETE = "incomplete" + MATURITY_EXPERIMENTAL = "experimental" + MATURITY_COMPLETE = "complete" + MATURITY_DEPRECATED = "deprecated" + + MATURITY_ALL = [MATURITY_INCOMPLETE, MATURITY_EXPERIMENTAL, + MATURITY_COMPLETE, MATURITY_DEPRECATED] + + def __init__(self, key, title, + notes=None, cli=None, maturity=None, + api_doc_link=None, admin_doc_link=None, + tempest_test_uuids=None): + """MatrixFeature models a row in the matrix + + This initialises ``self.implementations``, which is a dict of + (MatrixTarget.key, MatrixImplementation) + This is the list of cells for the given row of the matrix. + + :param key: used as the HTML id for the details, and so in the URL + :param title: human friendly short title, used in the matrix + :param notes: Arbitrarily long string describing the feature + :param cli: list of cli commands related to the feature + :param maturity: incomplete, experimental, complete or deprecated + for a full definition see the rst doc + :param api_doc_link: URL to the API ref for this feature + :param admin_doc_link: URL to the admin docs for using this feature + :param tempest_test_uuids: uuids for tests that validate this feature + """ + if cli is None: + cli = [] + if tempest_test_uuids is None: + tempest_test_uuids = [] + self.key = key + self.title = title + self.notes = notes + self.implementations = {} + self.cli = cli + self.maturity = maturity + self.api_doc_link = api_doc_link + self.admin_doc_link = admin_doc_link + self.tempest_test_uuids = tempest_test_uuids + + +class FeatureMatrixDirective(rst.Directive): + """The Sphinx directive plugin + + Has single required argument, the filename for the ini file. + + The usage of the directive looks like this:: + + .. feature_matrix:: feature_matrix_gp.ini + + """ + required_arguments = 1 + + def run(self): + matrix = self._load_feature_matrix() + return self._build_markup(matrix) + + def _load_feature_matrix(self): + """Reads the feature-matrix.ini file and populates an instance + of the Matrix class with all the data. + + :returns: Matrix instance + """ + + cfg = configparser.SafeConfigParser() + env = self.state.document.settings.env + filename = self.arguments[0] + rel_fpath, fpath = env.relfn2path(filename) + with open(fpath) as fp: + cfg.readfp(fp) + + # This ensures that the docs are rebuilt whenever the + # .ini file changes + env.note_dependency(rel_fpath) + + matrix = Matrix() + matrix.targets = self._get_targets(cfg) + matrix.features = self._get_features(cfg, matrix.targets) + + return matrix + + def _get_targets(self, cfg): + """The 'targets' section is special - it lists all the + hypervisors that this file records data for. + """ + + targets = {} + + for section in cfg.sections(): + if not section.startswith("target."): + continue + + key = section[7:] + title = cfg.get(section, "title") + link = cfg.get(section, "link") + driver = key.split("-")[0] + target = MatrixTarget(key, title, driver, link=link) + + targets[key] = target + + return targets + + def _get_features(self, cfg, targets): + """All sections except 'targets' describe some feature of + the Nova hypervisor driver implementation. + """ + + features = [] + + for section in cfg.sections(): + if section == "targets": + continue + if section.startswith("target."): + continue + if not cfg.has_option(section, "title"): + raise Exception( + "'title' field missing in '[%s]' section" % section) + + title = cfg.get(section, "title") + + maturity = MatrixFeature.MATURITY_INCOMPLETE + if cfg.has_option(section, "maturity"): + maturity = cfg.get(section, "maturity").lower() + if maturity not in MatrixFeature.MATURITY_ALL: + raise Exception( + "'maturity' field value '%s' in ['%s']" + "section must be %s" % + (maturity, section, + ",".join(MatrixFeature.MATURITY_ALL))) + + notes = None + if cfg.has_option(section, "notes"): + notes = cfg.get(section, "notes") + cli = [] + if cfg.has_option(section, "cli"): + cli = cfg.get(section, "cli") + api_doc_link = None + if cfg.has_option(section, "api_doc_link"): + api_doc_link = cfg.get(section, "api_doc_link") + admin_doc_link = None + if cfg.has_option(section, "admin_doc_link"): + admin_doc_link = cfg.get(section, "admin_doc_link") + tempest_test_uuids = [] + if cfg.has_option(section, "tempest_test_uuids"): + tempest_test_uuids = cfg.get(section, "tempest_test_uuids") + + feature = MatrixFeature(section, title, notes, cli, maturity, + api_doc_link, admin_doc_link, tempest_test_uuids) + + # Now we've got the basic feature details, we must process + # the hypervisor driver implementation for each feature + for item in cfg.options(section): + key = item.replace("driver-impl-", "") + + if key not in targets: + # TODO(johngarbutt) would be better to skip known list + if item.startswith("driver-impl-"): + raise Exception( + "Driver impl '%s' in '[%s]' not declared" % + (item, section)) + continue + + impl_status_and_release = cfg.get(section, item) + impl_status_and_release = impl_status_and_release.split(":") + impl_status = impl_status_and_release[0] + release = None + if len(impl_status_and_release) == 2: + release = impl_status_and_release[1] + + if impl_status not in MatrixImplementation.STATUS_ALL: + raise Exception( + "'%s' value '%s' in '[%s]' section must be %s" % + (item, impl_status, section, + ",".join(MatrixImplementation.STATUS_ALL))) + + noteskey = "driver-notes-" + item[12:] + notes = None + if cfg.has_option(section, noteskey): + notes = cfg.get(section, noteskey) + + target = targets[key] + impl = MatrixImplementation(impl_status, notes, release) + feature.implementations[target.key] = impl + + for key in targets: + if key not in feature.implementations: + raise Exception("'%s' missing in '[%s]' section" % + (key, section)) + + features.append(feature) + + return features + + def _build_markup(self, matrix): + """Constructs the docutils content for the feature matrix""" + content = [] + self._build_summary(matrix, content) + self._build_details(matrix, content) + return content + + def _build_summary(self, matrix, content): + """Constructs the docutils content for the summary of + the feature matrix. + + The summary consists of a giant table, with one row + for each feature, and a column for each hypervisor + driver. It provides an 'at a glance' summary of the + status of each driver + """ + + summarytitle = nodes.subtitle(text="Summary") + summary = nodes.table() + cols = len(matrix.targets.keys()) + cols += 2 + summarygroup = nodes.tgroup(cols=cols) + summarybody = nodes.tbody() + summaryhead = nodes.thead() + + for i in range(cols): + summarygroup.append(nodes.colspec(colwidth=1)) + summarygroup.append(summaryhead) + summarygroup.append(summarybody) + summary.append(summarygroup) + content.append(summarytitle) + content.append(summary) + + # This sets up all the column headers - two fixed + # columns for feature name & status + header = nodes.row() + blank = nodes.entry() + blank.append(nodes.emphasis(text="Feature")) + header.append(blank) + blank = nodes.entry() + blank.append(nodes.emphasis(text="Maturity")) + header.append(blank) + summaryhead.append(header) + + # then one column for each hypervisor driver + impls = matrix.targets.keys() + impls.sort() + for key in impls: + target = matrix.targets[key] + implcol = nodes.entry() + header.append(implcol) + if target.link: + uri = target.link + target_ref = nodes.reference("", refuri=uri) + target_txt = nodes.inline() + implcol.append(target_txt) + target_txt.append(target_ref) + target_ref.append(nodes.strong(text=target.title)) + else: + implcol.append(nodes.strong(text=target.title)) + + # We now produce the body of the table, one row for + # each feature to report on + for feature in matrix.features: + item = nodes.row() + + # the hyperlink target name linking to details + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key) + + # first the to fixed columns for title/status + keycol = nodes.entry() + item.append(keycol) + keyref = nodes.reference(refid=id) + keytxt = nodes.inline() + keycol.append(keytxt) + keytxt.append(keyref) + keyref.append(nodes.strong(text=feature.title)) + + maturitycol = nodes.entry() + item.append(maturitycol) + maturitycol.append(nodes.inline( + text=feature.maturity, + classes=["fm_maturity_" + feature.maturity])) + + # and then one column for each hypervisor driver + impls = matrix.targets.keys() + impls.sort() + for key in impls: + target = matrix.targets[key] + impl = feature.implementations[key] + implcol = nodes.entry() + item.append(implcol) + + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key + "_" + key) + + implref = nodes.reference(refid=id) + impltxt = nodes.inline() + implcol.append(impltxt) + impltxt.append(implref) + + impl_status = "" + if impl.status == MatrixImplementation.STATUS_COMPLETE: + impl_status = u"\u2714" + elif impl.status == MatrixImplementation.STATUS_MISSING: + impl_status = u"\u2716" + elif impl.status == MatrixImplementation.STATUS_PARTIAL: + impl_status = u"\u2714" + elif impl.status == MatrixImplementation.STATUS_UKNOWN: + impl_status = u"?" + + implref.append(nodes.literal( + text=impl_status, + classes=["fm_impl_summary", "fm_impl_" + impl.status])) + + if impl.release: + implref.append(nodes.inline(text=" %s" % impl.release)) + + summarybody.append(item) + + def _build_details(self, matrix, content): + """Constructs the docutils content for the details of + the feature matrix. + + This is generated as a bullet list of features. + Against each feature we provide the description of + the feature and then the details of the hypervisor + impls, with any driver specific notes that exist + """ + + detailstitle = nodes.subtitle(text="Details") + details = nodes.bullet_list() + + content.append(detailstitle) + content.append(details) + + # One list entry for each feature we're reporting on + for feature in matrix.features: + item = nodes.list_item() + + # The hypervisor target name linked from summary table + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key) + + # Highlight the feature title name + item.append(nodes.strong(text=feature.title, + ids=[id])) + + if feature.notes is not None: + para_notes = nodes.paragraph() + para_notes.append(nodes.inline(text=feature.notes)) + item.append(para_notes) + + self._add_feature_info(item, feature) + + if feature.cli: + item.append(self._create_cli_paragraph(feature)) + + para_divers = nodes.paragraph() + para_divers.append(nodes.strong(text="drivers:")) + # A sub-list giving details of each hypervisor target + impls = nodes.bullet_list() + for key in feature.implementations: + target = matrix.targets[key] + impl = feature.implementations[key] + subitem = nodes.list_item() + + id = re.sub("[^a-zA-Z0-9_]", "_", + feature.key + "_" + key) + subitem += [ + nodes.strong(text=target.title + ": "), + nodes.literal(text=impl.status, + classes=["fm_impl_" + impl.status], + ids=[id]), + ] + if impl.release: + release_letter = impl.release.upper() + release_text = \ + ' (updated in "%s" release)' % release_letter + subitem.append(nodes.inline(text=release_text)) + if impl.notes is not None: + subitem.append(self._create_notes_paragraph(impl.notes)) + impls.append(subitem) + + para_divers.append(impls) + item.append(para_divers) + details.append(item) + + def _add_feature_info(self, item, feature): + para_info = nodes.paragraph() + para_info.append(nodes.strong(text="info:")) + info_list = nodes.bullet_list() + + maturity_literal = nodes.literal(text=feature.maturity, + classes=["fm_maturity_" + feature.maturity]) + self._append_info_list_item(info_list, + "Maturity", items=[maturity_literal]) + self._append_info_list_item(info_list, + "API Docs", link=feature.api_doc_link) + self._append_info_list_item(info_list, + "Admin Docs", link=feature.admin_doc_link) + + tempest_items = [] + if feature.tempest_test_uuids: + for uuid in feature.tempest_test_uuids.split(";"): + base = "https://github.com/openstack/tempest/search?q=%s" + link = base % uuid + inline_ref = self._get_uri_ref(link, text=uuid) + tempest_items.append(inline_ref) + tempest_items.append(nodes.inline(text=", ")) + # removing trailing punctuation + tempest_items = tempest_items[:-1] + self._append_info_list_item(info_list, + "Tempest tests", items=tempest_items) + + para_info.append(info_list) + item.append(para_info) + + def _get_uri_ref(self, link, text=None): + if not text: + text = link + ref = nodes.reference("", text, refuri=link) + inline = nodes.inline() + inline.append(ref) + return inline + + def _append_info_list_item(self, info_list, title, + text=None, link=None, items=None): + subitem = nodes.list_item() + subitem.append(nodes.strong(text="%s: " % title)) + if items: + for item in items: + subitem.append(item) + elif link: + inline_link = self._get_uri_ref(link, text) + subitem.append(inline_link) + elif text: + subitem.append(nodes.literal(text=text)) + info_list.append(subitem) + + def _create_cli_paragraph(self, feature): + """Create a paragraph which represents the CLI commands of the feature + + The paragraph will have a bullet list of CLI commands. + """ + para = nodes.paragraph() + para.append(nodes.strong(text="CLI commands:")) + commands = nodes.bullet_list() + for c in feature.cli.split(";"): + cli_command = nodes.list_item() + cli_command += nodes.literal(text=c, classes=["fm_cli"]) + commands.append(cli_command) + para.append(commands) + return para + + def _create_notes_paragraph(self, notes): + """Constructs a paragraph which represents the implementation notes + + The paragraph consists of text and clickable URL nodes if links were + given in the notes. + """ + para = nodes.paragraph() + # links could start with http:// or https:// + link_idxs = [m.start() for m in re.finditer('https?://', notes)] + start_idx = 0 + for link_idx in link_idxs: + # assume the notes start with text (could be empty) + para.append(nodes.inline(text=notes[start_idx:link_idx])) + # create a URL node until the next text or the end of the notes + link_end_idx = notes.find(" ", link_idx) + if link_end_idx == -1: + # In case the notes end with a link without a blank + link_end_idx = len(notes) + uri = notes[link_idx:link_end_idx + 1] + para.append(nodes.reference("", uri, refuri=uri)) + start_idx = link_end_idx + 1 + + # get all text after the last link (could be empty) or all of the + # text if no link was given + para.append(nodes.inline(text=notes[start_idx:])) + return para + + +def setup(app): + app.add_directive('feature_matrix', FeatureMatrixDirective) + app.add_stylesheet('feature-matrix.css') diff --git a/doc/source/_static/feature-matrix.css b/doc/source/_static/feature-matrix.css new file mode 100644 index 000000000000..3b0cf5e82af7 --- /dev/null +++ b/doc/source/_static/feature-matrix.css @@ -0,0 +1,32 @@ +.fm_maturity_complete, +.fm_impl_complete { + color: rgb(0, 120, 0); + font-weight: normal; +} + +.fm_maturity_deprecated, +.fm_impl_missing { + color: rgb(120, 0, 0); + font-weight: normal; +} + +.fm_maturity_experimental, +.fm_impl_partial { + color: rgb(170, 170, 0); + font-weight: normal; +} + +.fm_maturity_incomplete, +.fm_impl_unknown { + color: rgb(170, 170, 170); + font-weight: normal; +} + +.fm_impl_summary { + font-size: 2em; +} + +.fm_cli { + font-family: monospace; + background-color: #F5F5F5; +} diff --git a/doc/source/conf.py b/doc/source/conf.py index e3cd90d4da8b..7249f97c77f0 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -36,7 +36,8 @@ extensions = ['sphinx.ext.autodoc', 'oslosphinx', "ext.support_matrix", 'oslo_config.sphinxconfiggen', - 'ext.versioned_notifications' + 'ext.versioned_notifications', + 'ext.feature_matrix', ] config_generator_config_file = '../../etc/nova/nova-config-generator.conf' diff --git a/doc/source/feature_classification.rst b/doc/source/feature_classification.rst index d51dadc054c0..6b81e12660ef 100644 --- a/doc/source/feature_classification.rst +++ b/doc/source/feature_classification.rst @@ -18,29 +18,69 @@ Feature Classification This document aims to define how we describe features listed in the :doc:`support-matrix`. +.. warning:: Please note: this is a work in progress! + Aims ==== Our users want the features they rely on to be reliable and always continue to solve for their use case. -When things break, users request that we solve their issues quickly. -It would be better if we never had those regressions in the first place. +The feature classification matrix should help identify which features are +complete and ready to use, and which should be used with caution. -We are taking a two-pronged approach: +An additional benefit, is we have a clear list of things where we need help +to complete the feature, its testing and its documentation. -* Tell our users what features are complete, well-documented, and are kept - stable by good tests. They will get a good experience if they stick to - using those features. - Please note that the tests are specific to particular combinations of - technologies. A deployment's choice of storage, networking and - hypervisor makes a big difference to what features will work. +Below is a matrix for a selection of important verticals: -* Get help for the features that are not in the above state, and warn our - users about the risks of using those features before they are ready. - It should make it much clearer how to help improve the feature. +* :ref:`matrix-gp` +* :ref:`matrix-nfv` +* :ref:`matrix-hpc` -Concepts -======== +For more details on the concepts in each matrix, +please see :ref:`notes-on-concepts`. + +.. _matrix-gp: + +General Purpose Cloud Features +=============================== + +This is a summary of the key features dev/test clouds, and other similar +general purpose clouds need, and it describes their current state. + +Below there are sections on NFV and HPC specific features. These look at +specific features and scenarios that are important to those more specific +sets of use cases. + +.. feature_matrix:: feature_matrix_gp.ini + +.. _matrix-nfv: + +NFV Cloud Features +================== + +Network Function Virtualization (NFV) is about virtualizing network node +functions into building blocks that may connect, or chain together to +create a particular service. It is common for this workloads needing +bare metal like performance, i.e. low latency and close to line speed +performance. + +.. feature_matrix:: feature_matrix_nfv.ini + +.. _matrix-hpc: + +HPC Cloud Features +================== + +High Performance Compute (HPC) cloud have some specific needs that are covered +in this set of features. + +.. feature_matrix:: feature_matrix_hpc.ini + +.. _notes-on-concepts: + +Notes on Concepts +================= Some definitions to help understand the later part of the document. @@ -151,22 +191,3 @@ The eventual goal is to automate this list from some third party CI reporting system, but so we can make progress, this will be a manual inspection that is documented by an hand written ini file. Ideally, this will be reviewed every milestone. - -Feature Group Definitions -========================= - -This is a look at features targeted at application developers, and the current -state of each feature, independent of the specific deployment. - -Please note: this is still a work in progress! - -Key TODOs: - -* use new API docs as a template for the feature groups, into ini file -* add lists of tempest UUIDs for each group -* link from hypervisor support matrix into feature group maturity ratings -* add maturity rating into the feature groups, with a justification, which - is likely to include lints to API docs, etc -* replace tick and cross in support matrix with "deployment ratings" -* eventually generate the tick and cross from live, historical, CI results - diff --git a/doc/source/feature_matrix_gp.ini b/doc/source/feature_matrix_gp.ini new file mode 100644 index 000000000000..c0eed6dc85f8 --- /dev/null +++ b/doc/source/feature_matrix_gp.ini @@ -0,0 +1,87 @@ +# +# Lists all the CI jobs as targets +# + +[target.libvirt-kvm] +title=libvirt+kvm (gate) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + +[target.xenserver] +title=XenServer CI +link=https://wiki.openstack.org/wiki/XenServer/XenServer_CI + +[target.vmware] +title=VMware CI +link=https://wiki.openstack.org/wiki/NovaVMware/Minesweeper + +[target.hyperv] +title=Hyper-V CI +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Hyper-V_CI + +[target.ironic] +title=Ironic CI +link= + +# +# Lists all features +# +# Includes information on the feature, its maturity, status, +# links to admin docs, api docs and tempest test uuids. +# +# It then lists the current state for each of the about CI jobs. +# It is hoped this mapping will eventually be automated. +# + +[operation.create-delete-server] +title=Create Server and Terminate Server +notes=TODO add notes for feature 1 +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref-compute-v2.1.html#createServer +admin_doc_link=http://docs.openstack.org/admin-guide-cloud/compute-images-instances.html#instance-building-blocks +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=complete:l +libvirt-xen=missing +xenserver=partial:k +vmware=missing +hyperv=missing +ironic=unknown + +[operation.snapshot-server] +title=Snapshot Server +notes=TODO add notes for feature 2 +maturity=complete +cli= +libvirt-kvm=complete:b +libvirt-xen=missing +xenserver=partial:c +vmware=missing +hyperv=complete:m +ironic=partial + +[operation.power-ops] +title=Server power ops +notes=TODO add notes for feature 3 +maturity=deprecated +cli= +libvirt-kvm=complete +libvirt-xen=missing +xenserver=partial +vmware=missing +hyperv=complete:m +ironic=unknown + +[operation.resize-server] +title=Resize Server +notes=TODO add notes for feature 4 +maturity=incomplete +cli= +libvirt-kvm=complete +libvirt-xen=missing +xenserver=partial +vmware=missing +hyperv=complete +ironic=unknown diff --git a/doc/source/feature_matrix_hpc.ini b/doc/source/feature_matrix_hpc.ini new file mode 100644 index 000000000000..6320b00642d6 --- /dev/null +++ b/doc/source/feature_matrix_hpc.ini @@ -0,0 +1,34 @@ +[target.libvirt-kvm] +title=libvirt+kvm (gate) +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + +[target.xenserver] +title=XenServer CI +link=https://wiki.openstack.org/wiki/XenServer/XenServer_CI + +[target.vmware] +title=VMware CI +link=https://wiki.openstack.org/wiki/NovaVMware/Minesweeper + +[target.hyperv] +title=Hyper-V CI +link=https://wiki.openstack.org/wiki/ThirdPartySystems/Hyper-V_CI + + +[operation.gpu-passthrough] +title=GPU Passthrough +notes=TODO add notes for feature +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref-compute-v2.1.html#createServer +admin_doc_link=http://docs.openstack.org/admin-guide-cloud/compute-images-instances.html#instance-building-blocks +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=complete:l +libvirt-xen=missing +xenserver=partial:k +vmware=missing +hyperv=missing +ironic=unknown diff --git a/doc/source/feature_matrix_nfv.ini b/doc/source/feature_matrix_nfv.ini new file mode 100644 index 000000000000..fb1566510a51 --- /dev/null +++ b/doc/source/feature_matrix_nfv.ini @@ -0,0 +1,18 @@ +[target.libvirt-kvm] +title=libvirt+kvm +link=http://docs.openstack.org/infra/manual/developers.html#project-gating + +[target.libvirt-xen] +title=libvirt+xen +link=https://wiki.openstack.org/wiki/ThirdPartySystems/XenProject_CI + + +[operation.numa-placement] +title=NUMA Placement +notes=TODO add notes for feature +maturity=experimental +api_doc_link=http://developer.openstack.org/api-ref-compute-v2.1.html#createServer +admin_doc_link=http://docs.openstack.org/admin-guide-cloud/compute-images-instances.html#instance-building-blocks +tempest_test_uuids=9a438d88-10c6-4bcd-8b5b-5b6e25e1346f;585e934c-448e-43c4-acbf-d06a9b899997 +libvirt-kvm=partial:m +libvirt-xen=missing