diff --git a/etc/oslo-config-generator/openvswitch_agent.ini b/etc/oslo-config-generator/openvswitch_agent.ini
index 79478d39957..3fab70d3921 100644
--- a/etc/oslo-config-generator/openvswitch_agent.ini
+++ b/etc/oslo-config-generator/openvswitch_agent.ini
@@ -3,5 +3,4 @@ output_file = etc/neutron/plugins/ml2/openvswitch_agent.ini.sample
 wrap_width = 79
 
 namespace = neutron.ml2.ovs.agent
-namespace = neutron.ml2.xenapi
 namespace = oslo.log
diff --git a/lower-constraints.txt b/lower-constraints.txt
index 2c22c6c5754..a0dada5f670 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -58,7 +58,6 @@ os-client-config==1.28.0
 os-ken==0.3.0
 os-service-types==1.7.0
 os-vif==1.15.1
-os-xenapi==0.3.4
 osc-lib==1.8.0
 oslo.cache==1.26.0
 oslo.concurrency==3.26.0
diff --git a/neutron/agent/linux/utils.py b/neutron/agent/linux/utils.py
index 73ad326bfe2..f2221a3cccb 100644
--- a/neutron/agent/linux/utils.py
+++ b/neutron/agent/linux/utils.py
@@ -36,7 +36,6 @@ from oslo_utils import fileutils
 import psutil
 
 from neutron._i18n import _
-from neutron.agent.linux import xenapi_root_helper
 from neutron.common import utils
 from neutron.conf.agent import common as config
 from neutron import wsgi
@@ -57,12 +56,8 @@ class RootwrapDaemonHelper(object):
     def get_client(cls):
         with cls.__lock:
             if cls.__client is None:
-                if (xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN ==
-                        cfg.CONF.AGENT.root_helper_daemon):
-                    cls.__client = xenapi_root_helper.XenAPIClient()
-                else:
-                    cls.__client = client.Client(
-                        shlex.split(cfg.CONF.AGENT.root_helper_daemon))
+                cls.__client = client.Client(
+                    shlex.split(cfg.CONF.AGENT.root_helper_daemon))
             return cls.__client
 
 
diff --git a/neutron/agent/linux/xenapi_root_helper.py b/neutron/agent/linux/xenapi_root_helper.py
deleted file mode 100644
index 9274dc7e493..00000000000
--- a/neutron/agent/linux/xenapi_root_helper.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright (c) 2016 Citrix System.
-# All Rights Reserved.
-#
-#    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.
-
-"""xenapi root helper
-
-For xenapi, we may need to run some commands in dom0 with additional privilege.
-This xenapi root helper contains the class of XenAPIClient to support it:
-XenAPIClient will keep a XenAPI session to dom0 and allow to run commands
-in dom0 via calling XenAPI plugin. The XenAPI plugin is responsible to
-determine whether a command is safe to execute.
-"""
-
-from os_xenapi.client import session
-from os_xenapi.client import XenAPI
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_rootwrap import cmd as oslo_rootwrap_cmd
-from oslo_serialization import jsonutils
-
-from neutron.conf.agent import xenapi_conf
-
-
-ROOT_HELPER_DAEMON_TOKEN = 'xenapi_root_helper'  # nosec
-
-RC_UNKNOWN_XENAPI_ERROR = 80
-MSG_UNAUTHORIZED = "Unauthorized command"
-MSG_NOT_FOUND = "Executable not found"
-XENAPI_PLUGIN_FAILURE_ID = "XENAPI_PLUGIN_FAILURE"
-
-LOG = logging.getLogger(__name__)
-xenapi_conf.register_xenapi_opts(cfg.CONF)
-
-
-class XenAPIClient(object):
-    def __init__(self):
-        self._session = self._create_session(
-            cfg.CONF.xenapi.connection_url,
-            cfg.CONF.xenapi.connection_username,
-            cfg.CONF.xenapi.connection_password)
-
-    def _call_plugin(self, plugin, fn, args):
-        return self._session.call_plugin(plugin, fn, args)
-
-    def _create_session(self, url, username, password):
-        return session.XenAPISession(url, username, password,
-                                     originator="neutron")
-
-    def _get_return_code(self, failure_details):
-        # The details will be as:
-        # [XENAPI_PLUGIN_FAILURE_ID, methodname, except_class_name, message]
-        # We can distinguish the error type by checking the message string.
-        if (len(failure_details) == 4 and
-                XENAPI_PLUGIN_FAILURE_ID == failure_details[0]):
-            if (MSG_UNAUTHORIZED == failure_details[3]):
-                return oslo_rootwrap_cmd.RC_UNAUTHORIZED
-            elif (MSG_NOT_FOUND == failure_details[3]):
-                return oslo_rootwrap_cmd.RC_NOEXECFOUND
-        # otherwise we get unexpected exception.
-        return RC_UNKNOWN_XENAPI_ERROR
-
-    def execute(self, cmd, stdin=None):
-        out = ""
-        err = ""
-        if cmd is None or len(cmd) == 0:
-            err = "No command specified."
-            return oslo_rootwrap_cmd.RC_NOCOMMAND, out, err
-        try:
-            result_raw = self._call_plugin(
-                'netwrap.py', 'run_command',
-                {'cmd': jsonutils.dumps(cmd),
-                 'cmd_input': jsonutils.dumps(stdin)})
-            result = jsonutils.loads(result_raw)
-            returncode = result['returncode']
-            out = result['out']
-            err = result['err']
-            return returncode, out, err
-        except XenAPI.Failure as failure:
-            LOG.exception('Failed to execute command: %s', cmd)
-            returncode = self._get_return_code(failure.details)
-            return returncode, out, err
diff --git a/neutron/conf/agent/common.py b/neutron/conf/agent/common.py
index 9cc12a8b453..ba021879a8a 100644
--- a/neutron/conf/agent/common.py
+++ b/neutron/conf/agent/common.py
@@ -105,10 +105,6 @@ in "daemon mode" which has been reported to improve performance at scale. For
 more information on running rootwrap in "daemon mode", see:
 
 https://docs.openstack.org/oslo.rootwrap/latest/user/usage.html#daemon-mode
-
-For the agent which needs to execute commands in Dom0 in the hypervisor of
-XenServer, this option should be set to 'xenapi_root_helper', so that it will
-keep a XenAPI session to pass commands to Dom0.
 """)),
 ]
 
diff --git a/neutron/conf/agent/xenapi_conf.py b/neutron/conf/agent/xenapi_conf.py
index ce9d095a7fa..a77a5416b6b 100644
--- a/neutron/conf/agent/xenapi_conf.py
+++ b/neutron/conf/agent/xenapi_conf.py
@@ -17,20 +17,26 @@ from oslo_config import cfg
 
 from neutron._i18n import _
 
-XENAPI_CONF_SECTION = 'xenapi'
 
+XENAPI_DEPRECATION_REASON = ('XenAPI support has been removed from Nova, it '
+                             'will be removed in X.')
 XENAPI_OPTS = [
     cfg.StrOpt('connection_url',
-               help=_("URL for connection to XenServer/Xen Cloud Platform.")),
+               help=_("URL for connection to XenServer/Xen Cloud Platform."),
+               deprecated_for_removal=True,
+               deprecated_since='Wallaby',
+               deprecated_reason=XENAPI_DEPRECATION_REASON),
     cfg.StrOpt('connection_username',
                help=_("Username for connection to XenServer/Xen Cloud "
-                      "Platform.")),
+                      "Platform."),
+               deprecated_for_removal=True,
+               deprecated_since='Wallaby',
+               deprecated_reason=XENAPI_DEPRECATION_REASON),
     cfg.StrOpt('connection_password',
                help=_("Password for connection to XenServer/Xen Cloud "
                       "Platform."),
-               secret=True)
+               secret=True,
+               deprecated_for_removal=True,
+               deprecated_since='Wallaby',
+               deprecated_reason=XENAPI_DEPRECATION_REASON)
 ]
-
-
-def register_xenapi_opts(cfg=cfg.CONF):
-    cfg.register_opts(XENAPI_OPTS, group=XENAPI_CONF_SECTION)
diff --git a/neutron/opts.py b/neutron/opts.py
index 7b044096b70..97df2f2ee45 100644
--- a/neutron/opts.py
+++ b/neutron/opts.py
@@ -31,7 +31,6 @@ import neutron.conf.agent.linux
 import neutron.conf.agent.metadata.config as meta_conf
 import neutron.conf.agent.ovs_conf
 import neutron.conf.agent.ovsdb_api
-import neutron.conf.agent.xenapi_conf
 import neutron.conf.common
 import neutron.conf.db.dvr_mac_db
 import neutron.conf.db.extraroute_db
@@ -341,10 +340,3 @@ def list_ironic_auth_opts():
                 opt_list.append(plugin_option)
     opt_list.sort(key=operator.attrgetter('name'))
     return [(IRONIC_GROUP, opt_list)]
-
-
-def list_xenapi_opts():
-    return [
-        ('xenapi',
-         neutron.conf.agent.xenapi_conf.XENAPI_OPTS)
-    ]
diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py
index 874f9d57f45..b3dc1aacf2f 100644
--- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py
+++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py
@@ -53,7 +53,6 @@ from neutron.agent.common import ovs_lib
 from neutron.agent.common import polling
 from neutron.agent.common import utils
 from neutron.agent.l2 import l2_agent_extensions_manager as ext_manager
-from neutron.agent.linux import xenapi_root_helper
 from neutron.agent import rpc as agent_rpc
 from neutron.agent import securitygroups_rpc as agent_sg_rpc
 from neutron.api.rpc.callbacks import resources
@@ -62,7 +61,6 @@ from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc
 from neutron.common import config
 from neutron.common import utils as n_utils
 from neutron.conf.agent import common as agent_config
-from neutron.conf.agent import xenapi_conf
 from neutron.conf import service as service_conf
 from neutron.plugins.ml2.drivers.agent import capabilities
 from neutron.plugins.ml2.drivers.l2pop.rpc_manager import l2population_rpc
@@ -2769,20 +2767,7 @@ def validate_tunnel_config(tunnel_types, local_ip):
             raise SystemExit(1)
 
 
-def prepare_xen_compute():
-    is_xen_compute_host = 'rootwrap-xen-dom0' in cfg.CONF.AGENT.root_helper \
-        or xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN == \
-        cfg.CONF.AGENT.root_helper_daemon
-    if is_xen_compute_host:
-        xenapi_conf.register_xenapi_opts()
-        # Force ip_lib to always use the root helper to ensure that ip
-        # commands target xen dom0 rather than domU.
-        cfg.CONF.register_opts(ip_lib.OPTS)
-        cfg.CONF.set_default('ip_lib_force_root', True)
-
-
 def main(bridge_classes):
-    prepare_xen_compute()
     ovs_capabilities.register()
     ext_manager.register_opts(cfg.CONF)
     agent_config.setup_privsep()
diff --git a/neutron/tests/unit/agent/linux/test_utils.py b/neutron/tests/unit/agent/linux/test_utils.py
index 308c7f0adad..68938726f71 100644
--- a/neutron/tests/unit/agent/linux/test_utils.py
+++ b/neutron/tests/unit/agent/linux/test_utils.py
@@ -40,16 +40,6 @@ class AgentUtilsExecuteTest(base.BaseTestCase):
         self.process.return_value.returncode = 0
         self.mock_popen = self.process.return_value.communicate
 
-    def test_xenapi_root_helper(self):
-        token = utils.xenapi_root_helper.ROOT_HELPER_DAEMON_TOKEN
-        self.config(group='AGENT', root_helper_daemon=token)
-        with mock.patch(
-                'neutron.agent.linux.utils.xenapi_root_helper.XenAPIClient')\
-                as mock_xenapi_class:
-            mock_client = mock_xenapi_class.return_value
-            cmd_client = utils.RootwrapDaemonHelper.get_client()
-            self.assertEqual(cmd_client, mock_client)
-
     def test_without_helper(self):
         expected = "%s\n" % self.test_file
         self.mock_popen.return_value = [expected, ""]
diff --git a/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py b/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py
deleted file mode 100644
index 6fff5d59828..00000000000
--- a/neutron/tests/unit/agent/linux/test_xenapi_root_helper.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright 2016 Citrix System.
-#
-#    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.
-
-from unittest import mock
-
-from oslo_config import cfg
-from oslo_rootwrap import cmd as oslo_rootwrap_cmd
-
-from neutron.agent.linux import xenapi_root_helper as helper
-from neutron.conf.agent import xenapi_conf
-from neutron.tests import base
-
-
-class TestXenapiRootHelper(base.BaseTestCase):
-    def _get_fake_xenapi_client(self):
-        class FakeXenapiClient(helper.XenAPIClient):
-            def __init__(self):
-                self._session = mock.MagicMock()
-
-        return FakeXenapiClient()
-
-    def setUp(self):
-        super(TestXenapiRootHelper, self).setUp()
-        conf = cfg.CONF
-        xenapi_conf.register_xenapi_opts(conf)
-
-    def test_get_return_code_unauthourized(self):
-        failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID,
-                           'run_command',
-                           'PluginError',
-                           helper.MSG_UNAUTHORIZED]
-        xenapi_client = self._get_fake_xenapi_client()
-        rc = xenapi_client._get_return_code(failure_details)
-        self.assertEqual(oslo_rootwrap_cmd.RC_UNAUTHORIZED, rc)
-
-    def test_get_return_code_noexecfound(self):
-        failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID,
-                           'run_command',
-                           'PluginError',
-                           helper.MSG_NOT_FOUND]
-        xenapi_client = self._get_fake_xenapi_client()
-        rc = xenapi_client._get_return_code(failure_details)
-        self.assertEqual(oslo_rootwrap_cmd.RC_NOEXECFOUND, rc)
-
-    def test_get_return_code_unknown_error(self):
-        failure_details = [helper.XENAPI_PLUGIN_FAILURE_ID,
-                           'run_command',
-                           'PluginError',
-                           'Any unknown error']
-        xenapi_client = self._get_fake_xenapi_client()
-        rc = xenapi_client._get_return_code(failure_details)
-        self.assertEqual(helper.RC_UNKNOWN_XENAPI_ERROR, rc)
-
-    def test_execute(self):
-        cmd = ["ovs-vsctl", "list-ports", "xapi2"]
-        expect_cmd_args = {'cmd': '["ovs-vsctl", "list-ports", "xapi2"]',
-                           'cmd_input': 'null'}
-        raw_result = '{"returncode": 0, "err": "", "out": "vif158.2"}'
-
-        with mock.patch.object(helper.XenAPIClient, "_call_plugin",
-                               return_value=raw_result) as mock_call_plugin:
-            xenapi_client = self._get_fake_xenapi_client()
-            rc, out, err = xenapi_client.execute(cmd)
-
-            mock_call_plugin.assert_called_once_with(
-                'netwrap.py', 'run_command', expect_cmd_args)
-            self.assertEqual(0, rc)
-            self.assertEqual("vif158.2", out)
-            self.assertEqual("", err)
-
-    def test_execute_nocommand(self):
-        cmd = []
-        xenapi_client = self._get_fake_xenapi_client()
-        rc, out, err = xenapi_client.execute(cmd)
-        self.assertEqual(oslo_rootwrap_cmd.RC_NOCOMMAND, rc)
diff --git a/releasenotes/notes/remove_xenapi_support-2cc2a4b9ad0ef01d.yaml b/releasenotes/notes/remove_xenapi_support-2cc2a4b9ad0ef01d.yaml
new file mode 100644
index 00000000000..29b480b3543
--- /dev/null
+++ b/releasenotes/notes/remove_xenapi_support-2cc2a4b9ad0ef01d.yaml
@@ -0,0 +1,7 @@
+---
+deprecations:
+  - |
+    Removed ``XenAPI`` support in Neutron. This driver is no longer supported
+    in Nova and Neutron.
+    The configuration options have been marked as "deprecated for removal" and
+    will be removed in X release.
diff --git a/requirements.txt b/requirements.txt
index df01105a133..5a1cb5b1e86 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -54,7 +54,6 @@ pyOpenSSL>=17.1.0 # Apache-2.0
 python-novaclient>=9.1.0 # Apache-2.0
 openstacksdk>=0.31.2 # Apache-2.0
 python-designateclient>=2.7.0 # Apache-2.0
-os-xenapi>=0.3.4 # Apache-2.0
 os-vif>=1.15.1 # Apache-2.0
 futurist>=1.2.0 # Apache-2.0
 tooz>=1.58.0 # Apache-2.0