We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Reinstalling mmonit pack fails as the venv is being used
Im sure this is due the sensor being run, so you cant create the virtualenv again as its being used.

Should the packs action check if there is sensors and try to stop them before reinstalling a pack?

```
  "stderr": "st2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Setting up virtualenv for pack \"mmonit\"\nst2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Creating virtualenv for pack \"mmonit\" in \"/opt/stackstorm/virtualenvs/mmonit\"\nst2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Creating virtualenv in \"/opt/stackstorm/virtualenvs/mmonit\" using Python binary \"/usr/bin/python2.7\"\nTraceback (most recent call last):\n  File \"/usr/lib/python2.7/dist-packages/st2actions/runners/python_action_wrapper.py\", line 116, in <module>\n    obj.run()\n  File \"/usr/lib/python2.7/dist-packages/st2actions/runners/python_action_wrapper.py\", line 61, in run\n    output = action.run(**self._parameters)\n  File \"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\", line 52, in run\n    self._setup_pack_virtualenv(pack_name=pack_name)\n  File \"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\", line 89, in _setup_pack_virtualenv\n    self._create_virtualenv(virtualenv_path=virtualenv_path)\n  File \"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\", line 125, in _create_virtualenv\n    (virtualenv_path, stderr))\nException: Failed to create virtualenv in \"/opt/stackstorm/virtualenvs/mmonit\": Traceback (most recent call last):\n  File \"/usr/local/lib/python2.7/dist-packages/virtualenv.py\", line 2363, in <module>\n    main()\n  File \"/usr/local/lib/python2.7/dist-packages/virtualenv.py\", line 832, in main\n    symlink=options.symlink)\n  File \"/usr/local/lib/python2.7/dist-packages/virtualenv.py\", line 994, in create_environment\n    site_packages=site_packages, clear=clear, symlink=symlink))\n  File \"/usr/local/lib/python2.7/dist-packages/virtualenv.py\", line 1288, in install_python\n    shutil.copyfile(executable, py_executable)\n  File \"/usr/lib/python2.7/shutil.py\", line 83, in copyfile\n    with open(dst, 'wb') as fdst:\nIOError: [Errno 26] Text file busy: '/opt/stackstorm/virtualenvs/mmonit/bin/python2.7'\n\n",
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `contrib/packs/actions/pack_mgmt/setup_virtualenv.py`
Content:
```
1 # Licensed to the StackStorm, Inc ('StackStorm') under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import os
17 import re
18 
19 from oslo_config import cfg
20 
21 from st2common.util.shell import run_command
22 from st2actions.runners.pythonrunner import Action
23 from st2common.constants.pack import PACK_NAME_WHITELIST
24 from st2common.constants.pack import BASE_PACK_REQUIREMENTS
25 from st2common.content.utils import get_packs_base_paths
26 from st2common.content.utils import get_pack_directory
27 from st2common.util.shell import quote_unix
28 
29 
30 class SetupVirtualEnvironmentAction(Action):
31     """
32     Action which sets up virtual environment for the provided packs.
33 
34     Setup consists of the following step:
35 
36     1. Create virtual environment for the pack
37     2. Install base requirements which are common to all the packs
38     3. Install pack-specific requirements (if any)
39     """
40 
41     def __init__(self, config=None):
42         super(SetupVirtualEnvironmentAction, self).__init__(config=config)
43         self._base_virtualenvs_path = os.path.join(cfg.CONF.system.base_path,
44                                                    'virtualenvs/')
45 
46     def run(self, packs):
47         """
48         :param packs: A list of packs to create the environment for.
49         :type: packs: ``list``
50         """
51         for pack_name in packs:
52             self._setup_pack_virtualenv(pack_name=pack_name)
53 
54         message = ('Successfuly set up virtualenv for the following packs: %s' %
55                    (', '.join(packs)))
56         return message
57 
58     def _setup_pack_virtualenv(self, pack_name):
59         """
60         Setup virtual environment for the provided pack.
61 
62         :param pack_name: Pack name.
63         :type pack_name: ``str``
64         """
65         # Prevent directory traversal by whitelisting allowed characters in the
66         # pack name
67         if not re.match(PACK_NAME_WHITELIST, pack_name):
68             raise ValueError('Invalid pack name "%s"' % (pack_name))
69 
70         self.logger.debug('Setting up virtualenv for pack "%s"' % (pack_name))
71 
72         virtualenv_path = os.path.join(self._base_virtualenvs_path, quote_unix(pack_name))
73 
74         # Ensure pack directory exists in one of the search paths
75         pack_path = get_pack_directory(pack_name=pack_name)
76 
77         if not pack_path:
78             packs_base_paths = get_packs_base_paths()
79             search_paths = ', '.join(packs_base_paths)
80             msg = 'Pack "%s" is not installed. Looked in: %s' % (pack_name, search_paths)
81             raise Exception(msg)
82 
83         if not os.path.exists(self._base_virtualenvs_path):
84             os.makedirs(self._base_virtualenvs_path)
85 
86         # 1. Create virtual environment
87         self.logger.debug('Creating virtualenv for pack "%s" in "%s"' %
88                           (pack_name, virtualenv_path))
89         self._create_virtualenv(virtualenv_path=virtualenv_path)
90 
91         # 2. Install base requirements which are common to all the packs
92         self.logger.debug('Installing base requirements')
93         for requirement in BASE_PACK_REQUIREMENTS:
94             self._install_requirement(virtualenv_path=virtualenv_path,
95                                       requirement=requirement)
96 
97         # 3. Install pack-specific requirements
98         requirements_file_path = os.path.join(pack_path, 'requirements.txt')
99         has_requirements = os.path.isfile(requirements_file_path)
100 
101         if has_requirements:
102             self.logger.debug('Installing pack specific requirements from "%s"' %
103                               (requirements_file_path))
104             self._install_requirements(virtualenv_path, requirements_file_path)
105         else:
106             self.logger.debug('No pack specific requirements found')
107 
108         self.logger.debug('Virtualenv for pack "%s" successfully created in "%s"' %
109                           (pack_name, virtualenv_path))
110 
111     def _create_virtualenv(self, virtualenv_path):
112         python_binary = cfg.CONF.actionrunner.python_binary
113 
114         if not os.path.isfile(python_binary):
115             raise Exception('Python binary "%s" doesn\'t exist' % (python_binary))
116 
117         self.logger.debug('Creating virtualenv in "%s" using Python binary "%s"' %
118                           (virtualenv_path, python_binary))
119 
120         cmd = ['virtualenv', '-p', python_binary, '--system-site-packages', virtualenv_path]
121         exit_code, _, stderr = run_command(cmd=cmd)
122 
123         if exit_code != 0:
124             raise Exception('Failed to create virtualenv in "%s": %s' %
125                             (virtualenv_path, stderr))
126 
127         return True
128 
129     def _install_requirements(self, virtualenv_path, requirements_file_path):
130         """
131         Install requirements from a file.
132         """
133         pip_path = os.path.join(virtualenv_path, 'bin/pip')
134         cmd = [pip_path, 'install', '-U', '-r', requirements_file_path]
135         env = self._get_env_for_subprocess_command()
136         exit_code, stdout, stderr = run_command(cmd=cmd, env=env)
137 
138         if exit_code != 0:
139             raise Exception('Failed to install requirements from "%s": %s' %
140                             (requirements_file_path, stdout))
141 
142         return True
143 
144     def _install_requirement(self, virtualenv_path, requirement):
145         """
146         Install a single requirement.
147         """
148         pip_path = os.path.join(virtualenv_path, 'bin/pip')
149         cmd = [pip_path, 'install', requirement]
150         env = self._get_env_for_subprocess_command()
151         exit_code, stdout, stderr = run_command(cmd=cmd, env=env)
152 
153         if exit_code != 0:
154             raise Exception('Failed to install requirement "%s": %s' %
155                             (requirement, stdout))
156 
157         return True
158 
159     def _get_env_for_subprocess_command(self):
160         """
161         Retrieve environment to be used with the subprocess command.
162 
163         Note: We remove PYTHONPATH from the environment so the command works
164         correctly with the newely created virtualenv.
165         """
166         env = os.environ.copy()
167 
168         if 'PYTHONPATH' in env:
169             del env['PYTHONPATH']
170 
171         return env
172
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/contrib/packs/actions/pack_mgmt/setup_virtualenv.py b/contrib/packs/actions/pack_mgmt/setup_virtualenv.py
--- a/contrib/packs/actions/pack_mgmt/setup_virtualenv.py
+++ b/contrib/packs/actions/pack_mgmt/setup_virtualenv.py
@@ -15,6 +15,7 @@
 
 import os
 import re
+import shutil
 
 from oslo_config import cfg
 
@@ -83,6 +84,9 @@
         if not os.path.exists(self._base_virtualenvs_path):
             os.makedirs(self._base_virtualenvs_path)
 
+        # 0. Delete virtual environment if it exists
+        self._remove_virtualenv(virtualenv_path=virtualenv_path)
+
         # 1. Create virtual environment
         self.logger.debug('Creating virtualenv for pack "%s" in "%s"' %
                           (pack_name, virtualenv_path))
@@ -126,6 +130,20 @@
 
         return True
 
+    def _remove_virtualenv(self, virtualenv_path):
+        if not os.path.exists(virtualenv_path):
+            self.logger.info('Virtualenv path "%s" doesn\'t exist' % virtualenv_path)
+            return True
+
+        self.logger.debug('Removing virtualenv in "%s"' % virtualenv_path)
+        try:
+            shutil.rmtree(virtualenv_path)
+        except Exception as error:
+            self.logger.error('Error while removing virtualenv at "%s": "%s"' %
+                              (virtualenv_path, error))
+            raise
+        return True
+
     def _install_requirements(self, virtualenv_path, requirements_file_path):
         """
         Install requirements from a file.

{"golden_diff": "diff --git a/contrib/packs/actions/pack_mgmt/setup_virtualenv.py b/contrib/packs/actions/pack_mgmt/setup_virtualenv.py\n--- a/contrib/packs/actions/pack_mgmt/setup_virtualenv.py\n+++ b/contrib/packs/actions/pack_mgmt/setup_virtualenv.py\n@@ -15,6 +15,7 @@\n \n import os\n import re\n+import shutil\n \n from oslo_config import cfg\n \n@@ -83,6 +84,9 @@\n         if not os.path.exists(self._base_virtualenvs_path):\n             os.makedirs(self._base_virtualenvs_path)\n \n+        # 0. Delete virtual environment if it exists\n+        self._remove_virtualenv(virtualenv_path=virtualenv_path)\n+\n         # 1. Create virtual environment\n         self.logger.debug('Creating virtualenv for pack \"%s\" in \"%s\"' %\n                           (pack_name, virtualenv_path))\n@@ -126,6 +130,20 @@\n \n         return True\n \n+    def _remove_virtualenv(self, virtualenv_path):\n+        if not os.path.exists(virtualenv_path):\n+            self.logger.info('Virtualenv path \"%s\" doesn\\'t exist' % virtualenv_path)\n+            return True\n+\n+        self.logger.debug('Removing virtualenv in \"%s\"' % virtualenv_path)\n+        try:\n+            shutil.rmtree(virtualenv_path)\n+        except Exception as error:\n+            self.logger.error('Error while removing virtualenv at \"%s\": \"%s\"' %\n+                              (virtualenv_path, error))\n+            raise\n+        return True\n+\n     def _install_requirements(self, virtualenv_path, requirements_file_path):\n         \"\"\"\n         Install requirements from a file.\n", "issue": "Reinstalling mmonit pack fails as the venv is being used\nIm sure this is due the sensor being run, so you cant create the virtualenv again as its being used.\n\nShould the packs action check if there is sensors and try to stop them before reinstalling a pack?\n\n```\n  \"stderr\": \"st2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Setting up virtualenv for pack \\\"mmonit\\\"\\nst2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Creating virtualenv for pack \\\"mmonit\\\" in \\\"/opt/stackstorm/virtualenvs/mmonit\\\"\\nst2.actions.python.SetupVirtualEnvironmentAction: DEBUG    Creating virtualenv in \\\"/opt/stackstorm/virtualenvs/mmonit\\\" using Python binary \\\"/usr/bin/python2.7\\\"\\nTraceback (most recent call last):\\n  File \\\"/usr/lib/python2.7/dist-packages/st2actions/runners/python_action_wrapper.py\\\", line 116, in <module>\\n    obj.run()\\n  File \\\"/usr/lib/python2.7/dist-packages/st2actions/runners/python_action_wrapper.py\\\", line 61, in run\\n    output = action.run(**self._parameters)\\n  File \\\"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\\\", line 52, in run\\n    self._setup_pack_virtualenv(pack_name=pack_name)\\n  File \\\"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\\\", line 89, in _setup_pack_virtualenv\\n    self._create_virtualenv(virtualenv_path=virtualenv_path)\\n  File \\\"/opt/stackstorm/packs/packs/actions/pack_mgmt/setup_virtualenv.py\\\", line 125, in _create_virtualenv\\n    (virtualenv_path, stderr))\\nException: Failed to create virtualenv in \\\"/opt/stackstorm/virtualenvs/mmonit\\\": Traceback (most recent call last):\\n  File \\\"/usr/local/lib/python2.7/dist-packages/virtualenv.py\\\", line 2363, in <module>\\n    main()\\n  File \\\"/usr/local/lib/python2.7/dist-packages/virtualenv.py\\\", line 832, in main\\n    symlink=options.symlink)\\n  File \\\"/usr/local/lib/python2.7/dist-packages/virtualenv.py\\\", line 994, in create_environment\\n    site_packages=site_packages, clear=clear, symlink=symlink))\\n  File \\\"/usr/local/lib/python2.7/dist-packages/virtualenv.py\\\", line 1288, in install_python\\n    shutil.copyfile(executable, py_executable)\\n  File \\\"/usr/lib/python2.7/shutil.py\\\", line 83, in copyfile\\n    with open(dst, 'wb') as fdst:\\nIOError: [Errno 26] Text file busy: '/opt/stackstorm/virtualenvs/mmonit/bin/python2.7'\\n\\n\",\n```\n\n", "before_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport re\n\nfrom oslo_config import cfg\n\nfrom st2common.util.shell import run_command\nfrom st2actions.runners.pythonrunner import Action\nfrom st2common.constants.pack import PACK_NAME_WHITELIST\nfrom st2common.constants.pack import BASE_PACK_REQUIREMENTS\nfrom st2common.content.utils import get_packs_base_paths\nfrom st2common.content.utils import get_pack_directory\nfrom st2common.util.shell import quote_unix\n\n\nclass SetupVirtualEnvironmentAction(Action):\n    \"\"\"\n    Action which sets up virtual environment for the provided packs.\n\n    Setup consists of the following step:\n\n    1. Create virtual environment for the pack\n    2. Install base requirements which are common to all the packs\n    3. Install pack-specific requirements (if any)\n    \"\"\"\n\n    def __init__(self, config=None):\n        super(SetupVirtualEnvironmentAction, self).__init__(config=config)\n        self._base_virtualenvs_path = os.path.join(cfg.CONF.system.base_path,\n                                                   'virtualenvs/')\n\n    def run(self, packs):\n        \"\"\"\n        :param packs: A list of packs to create the environment for.\n        :type: packs: ``list``\n        \"\"\"\n        for pack_name in packs:\n            self._setup_pack_virtualenv(pack_name=pack_name)\n\n        message = ('Successfuly set up virtualenv for the following packs: %s' %\n                   (', '.join(packs)))\n        return message\n\n    def _setup_pack_virtualenv(self, pack_name):\n        \"\"\"\n        Setup virtual environment for the provided pack.\n\n        :param pack_name: Pack name.\n        :type pack_name: ``str``\n        \"\"\"\n        # Prevent directory traversal by whitelisting allowed characters in the\n        # pack name\n        if not re.match(PACK_NAME_WHITELIST, pack_name):\n            raise ValueError('Invalid pack name \"%s\"' % (pack_name))\n\n        self.logger.debug('Setting up virtualenv for pack \"%s\"' % (pack_name))\n\n        virtualenv_path = os.path.join(self._base_virtualenvs_path, quote_unix(pack_name))\n\n        # Ensure pack directory exists in one of the search paths\n        pack_path = get_pack_directory(pack_name=pack_name)\n\n        if not pack_path:\n            packs_base_paths = get_packs_base_paths()\n            search_paths = ', '.join(packs_base_paths)\n            msg = 'Pack \"%s\" is not installed. Looked in: %s' % (pack_name, search_paths)\n            raise Exception(msg)\n\n        if not os.path.exists(self._base_virtualenvs_path):\n            os.makedirs(self._base_virtualenvs_path)\n\n        # 1. Create virtual environment\n        self.logger.debug('Creating virtualenv for pack \"%s\" in \"%s\"' %\n                          (pack_name, virtualenv_path))\n        self._create_virtualenv(virtualenv_path=virtualenv_path)\n\n        # 2. Install base requirements which are common to all the packs\n        self.logger.debug('Installing base requirements')\n        for requirement in BASE_PACK_REQUIREMENTS:\n            self._install_requirement(virtualenv_path=virtualenv_path,\n                                      requirement=requirement)\n\n        # 3. Install pack-specific requirements\n        requirements_file_path = os.path.join(pack_path, 'requirements.txt')\n        has_requirements = os.path.isfile(requirements_file_path)\n\n        if has_requirements:\n            self.logger.debug('Installing pack specific requirements from \"%s\"' %\n                              (requirements_file_path))\n            self._install_requirements(virtualenv_path, requirements_file_path)\n        else:\n            self.logger.debug('No pack specific requirements found')\n\n        self.logger.debug('Virtualenv for pack \"%s\" successfully created in \"%s\"' %\n                          (pack_name, virtualenv_path))\n\n    def _create_virtualenv(self, virtualenv_path):\n        python_binary = cfg.CONF.actionrunner.python_binary\n\n        if not os.path.isfile(python_binary):\n            raise Exception('Python binary \"%s\" doesn\\'t exist' % (python_binary))\n\n        self.logger.debug('Creating virtualenv in \"%s\" using Python binary \"%s\"' %\n                          (virtualenv_path, python_binary))\n\n        cmd = ['virtualenv', '-p', python_binary, '--system-site-packages', virtualenv_path]\n        exit_code, _, stderr = run_command(cmd=cmd)\n\n        if exit_code != 0:\n            raise Exception('Failed to create virtualenv in \"%s\": %s' %\n                            (virtualenv_path, stderr))\n\n        return True\n\n    def _install_requirements(self, virtualenv_path, requirements_file_path):\n        \"\"\"\n        Install requirements from a file.\n        \"\"\"\n        pip_path = os.path.join(virtualenv_path, 'bin/pip')\n        cmd = [pip_path, 'install', '-U', '-r', requirements_file_path]\n        env = self._get_env_for_subprocess_command()\n        exit_code, stdout, stderr = run_command(cmd=cmd, env=env)\n\n        if exit_code != 0:\n            raise Exception('Failed to install requirements from \"%s\": %s' %\n                            (requirements_file_path, stdout))\n\n        return True\n\n    def _install_requirement(self, virtualenv_path, requirement):\n        \"\"\"\n        Install a single requirement.\n        \"\"\"\n        pip_path = os.path.join(virtualenv_path, 'bin/pip')\n        cmd = [pip_path, 'install', requirement]\n        env = self._get_env_for_subprocess_command()\n        exit_code, stdout, stderr = run_command(cmd=cmd, env=env)\n\n        if exit_code != 0:\n            raise Exception('Failed to install requirement \"%s\": %s' %\n                            (requirement, stdout))\n\n        return True\n\n    def _get_env_for_subprocess_command(self):\n        \"\"\"\n        Retrieve environment to be used with the subprocess command.\n\n        Note: We remove PYTHONPATH from the environment so the command works\n        correctly with the newely created virtualenv.\n        \"\"\"\n        env = os.environ.copy()\n\n        if 'PYTHONPATH' in env:\n            del env['PYTHONPATH']\n\n        return env\n", "path": "contrib/packs/actions/pack_mgmt/setup_virtualenv.py"}], "after_files": [{"content": "# Licensed to the StackStorm, Inc ('StackStorm') under one or more\n# contributor license agreements.  See the NOTICE file distributed with\n# this work for additional information regarding copyright ownership.\n# The ASF licenses this file to You under the Apache License, Version 2.0\n# (the \"License\"); you may not use this file except in compliance with\n# the License.  You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport re\nimport shutil\n\nfrom oslo_config import cfg\n\nfrom st2common.util.shell import run_command\nfrom st2actions.runners.pythonrunner import Action\nfrom st2common.constants.pack import PACK_NAME_WHITELIST\nfrom st2common.constants.pack import BASE_PACK_REQUIREMENTS\nfrom st2common.content.utils import get_packs_base_paths\nfrom st2common.content.utils import get_pack_directory\nfrom st2common.util.shell import quote_unix\n\n\nclass SetupVirtualEnvironmentAction(Action):\n    \"\"\"\n    Action which sets up virtual environment for the provided packs.\n\n    Setup consists of the following step:\n\n    1. Create virtual environment for the pack\n    2. Install base requirements which are common to all the packs\n    3. Install pack-specific requirements (if any)\n    \"\"\"\n\n    def __init__(self, config=None):\n        super(SetupVirtualEnvironmentAction, self).__init__(config=config)\n        self._base_virtualenvs_path = os.path.join(cfg.CONF.system.base_path,\n                                                   'virtualenvs/')\n\n    def run(self, packs):\n        \"\"\"\n        :param packs: A list of packs to create the environment for.\n        :type: packs: ``list``\n        \"\"\"\n        for pack_name in packs:\n            self._setup_pack_virtualenv(pack_name=pack_name)\n\n        message = ('Successfuly set up virtualenv for the following packs: %s' %\n                   (', '.join(packs)))\n        return message\n\n    def _setup_pack_virtualenv(self, pack_name):\n        \"\"\"\n        Setup virtual environment for the provided pack.\n\n        :param pack_name: Pack name.\n        :type pack_name: ``str``\n        \"\"\"\n        # Prevent directory traversal by whitelisting allowed characters in the\n        # pack name\n        if not re.match(PACK_NAME_WHITELIST, pack_name):\n            raise ValueError('Invalid pack name \"%s\"' % (pack_name))\n\n        self.logger.debug('Setting up virtualenv for pack \"%s\"' % (pack_name))\n\n        virtualenv_path = os.path.join(self._base_virtualenvs_path, quote_unix(pack_name))\n\n        # Ensure pack directory exists in one of the search paths\n        pack_path = get_pack_directory(pack_name=pack_name)\n\n        if not pack_path:\n            packs_base_paths = get_packs_base_paths()\n            search_paths = ', '.join(packs_base_paths)\n            msg = 'Pack \"%s\" is not installed. Looked in: %s' % (pack_name, search_paths)\n            raise Exception(msg)\n\n        if not os.path.exists(self._base_virtualenvs_path):\n            os.makedirs(self._base_virtualenvs_path)\n\n        # 0. Delete virtual environment if it exists\n        self._remove_virtualenv(virtualenv_path=virtualenv_path)\n\n        # 1. Create virtual environment\n        self.logger.debug('Creating virtualenv for pack \"%s\" in \"%s\"' %\n                          (pack_name, virtualenv_path))\n        self._create_virtualenv(virtualenv_path=virtualenv_path)\n\n        # 2. Install base requirements which are common to all the packs\n        self.logger.debug('Installing base requirements')\n        for requirement in BASE_PACK_REQUIREMENTS:\n            self._install_requirement(virtualenv_path=virtualenv_path,\n                                      requirement=requirement)\n\n        # 3. Install pack-specific requirements\n        requirements_file_path = os.path.join(pack_path, 'requirements.txt')\n        has_requirements = os.path.isfile(requirements_file_path)\n\n        if has_requirements:\n            self.logger.debug('Installing pack specific requirements from \"%s\"' %\n                              (requirements_file_path))\n            self._install_requirements(virtualenv_path, requirements_file_path)\n        else:\n            self.logger.debug('No pack specific requirements found')\n\n        self.logger.debug('Virtualenv for pack \"%s\" successfully created in \"%s\"' %\n                          (pack_name, virtualenv_path))\n\n    def _create_virtualenv(self, virtualenv_path):\n        python_binary = cfg.CONF.actionrunner.python_binary\n\n        if not os.path.isfile(python_binary):\n            raise Exception('Python binary \"%s\" doesn\\'t exist' % (python_binary))\n\n        self.logger.debug('Creating virtualenv in \"%s\" using Python binary \"%s\"' %\n                          (virtualenv_path, python_binary))\n\n        cmd = ['virtualenv', '-p', python_binary, '--system-site-packages', virtualenv_path]\n        exit_code, _, stderr = run_command(cmd=cmd)\n\n        if exit_code != 0:\n            raise Exception('Failed to create virtualenv in \"%s\": %s' %\n                            (virtualenv_path, stderr))\n\n        return True\n\n    def _remove_virtualenv(self, virtualenv_path):\n        if not os.path.exists(virtualenv_path):\n            self.logger.info('Virtualenv path \"%s\" doesn\\'t exist' % virtualenv_path)\n            return True\n\n        self.logger.debug('Removing virtualenv in \"%s\"' % virtualenv_path)\n        try:\n            shutil.rmtree(virtualenv_path)\n        except Exception as error:\n            self.logger.error('Error while removing virtualenv at \"%s\": \"%s\"' %\n                              (virtualenv_path, error))\n            raise\n        return True\n\n    def _install_requirements(self, virtualenv_path, requirements_file_path):\n        \"\"\"\n        Install requirements from a file.\n        \"\"\"\n        pip_path = os.path.join(virtualenv_path, 'bin/pip')\n        cmd = [pip_path, 'install', '-U', '-r', requirements_file_path]\n        env = self._get_env_for_subprocess_command()\n        exit_code, stdout, stderr = run_command(cmd=cmd, env=env)\n\n        if exit_code != 0:\n            raise Exception('Failed to install requirements from \"%s\": %s' %\n                            (requirements_file_path, stdout))\n\n        return True\n\n    def _install_requirement(self, virtualenv_path, requirement):\n        \"\"\"\n        Install a single requirement.\n        \"\"\"\n        pip_path = os.path.join(virtualenv_path, 'bin/pip')\n        cmd = [pip_path, 'install', requirement]\n        env = self._get_env_for_subprocess_command()\n        exit_code, stdout, stderr = run_command(cmd=cmd, env=env)\n\n        if exit_code != 0:\n            raise Exception('Failed to install requirement \"%s\": %s' %\n                            (requirement, stdout))\n\n        return True\n\n    def _get_env_for_subprocess_command(self):\n        \"\"\"\n        Retrieve environment to be used with the subprocess command.\n\n        Note: We remove PYTHONPATH from the environment so the command works\n        correctly with the newely created virtualenv.\n        \"\"\"\n        env = os.environ.copy()\n\n        if 'PYTHONPATH' in env:\n            del env['PYTHONPATH']\n\n        return env\n", "path": "contrib/packs/actions/pack_mgmt/setup_virtualenv.py"}]}