We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add a `region` arg to `OpenStackLatentWorker`
Would be nice to allow passing a region_name to novaclient in `OpenStackLatentWorker`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `master/buildbot/worker/openstack.py`
Content:
```
1 # This file is part of Buildbot.  Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Portions Copyright Buildbot Team Members
15 # Portions Copyright 2013 Cray Inc.
16 
17 from __future__ import absolute_import
18 from __future__ import division
19 from __future__ import print_function
20 
21 import math
22 import time
23 
24 from twisted.internet import defer
25 from twisted.internet import threads
26 from twisted.python import log
27 
28 from buildbot import config
29 from buildbot.interfaces import LatentWorkerFailedToSubstantiate
30 from buildbot.worker import AbstractLatentWorker
31 
32 try:
33     from keystoneauth1 import loading
34     from keystoneauth1 import session
35     from novaclient import client
36     from novaclient.exceptions import NotFound
37     _hush_pyflakes = [client]
38 except ImportError:
39     NotFound = Exception
40     client = None
41     loading = None
42     session = None
43 
44 
45 ACTIVE = 'ACTIVE'
46 BUILD = 'BUILD'
47 DELETED = 'DELETED'
48 UNKNOWN = 'UNKNOWN'
49 
50 
51 class OpenStackLatentWorker(AbstractLatentWorker):
52 
53     instance = None
54     _poll_resolution = 5  # hook point for tests
55 
56     def __init__(self, name, password,
57                  flavor,
58                  os_username,
59                  os_password,
60                  os_tenant_name,
61                  os_auth_url,
62                  block_devices=None,
63                  image=None,
64                  meta=None,
65                  # Have a nova_args parameter to allow passing things directly
66                  # to novaclient.
67                  nova_args=None,
68                  client_version='2',
69                  **kwargs):
70 
71         if not client:
72             config.error("The python module 'novaclient' is needed  "
73                          "to use a OpenStackLatentWorker. "
74                          "Please install 'python-novaclient' package.")
75         if not loading or not session:
76             config.error("The python module 'keystoneauth1' is needed "
77                          "to use a OpenStackLatentWorker. "
78                          "Please install the 'keystoneauth1' package.")
79 
80         if not block_devices and not image:
81             raise ValueError('One of block_devices or image must be given')
82 
83         AbstractLatentWorker.__init__(self, name, password, **kwargs)
84 
85         self.flavor = flavor
86         self.client_version = client_version
87         if client:
88             self.novaclient = self._constructClient(
89                 client_version, os_username, os_password, os_tenant_name,
90                 os_auth_url)
91 
92         if block_devices is not None:
93             self.block_devices = [
94                 self._parseBlockDevice(bd) for bd in block_devices]
95         else:
96             self.block_devices = None
97         self.image = image
98         self.meta = meta
99         self.nova_args = nova_args if nova_args is not None else {}
100 
101     @staticmethod
102     def _constructClient(client_version, username, password, project_name,
103                          auth_url):
104         """Return a novaclient from the given args."""
105         loader = loading.get_plugin_loader('password')
106         auth = loader.load_from_options(auth_url=auth_url, username=username,
107                                         password=password, project_name=project_name)
108         sess = session.Session(auth=auth)
109         return client.Client(client_version, session=sess)
110 
111     def _parseBlockDevice(self, block_device):
112         """
113         Parse a higher-level view of the block device mapping into something
114         novaclient wants. This should be similar to how Horizon presents it.
115         Required keys:
116             device_name: The name of the device; e.g. vda or xda.
117             source_type: image, snapshot, volume, or blank/None.
118             destination_type: Destination of block device: volume or local.
119             delete_on_termination: True/False.
120             uuid: The image, snapshot, or volume id.
121             boot_index: Integer used for boot order.
122             volume_size: Size of the device in GiB.
123         """
124         client_block_device = {}
125         client_block_device['device_name'] = block_device.get(
126             'device_name', 'vda')
127         client_block_device['source_type'] = block_device.get(
128             'source_type', 'image')
129         client_block_device['destination_type'] = block_device.get(
130             'destination_type', 'volume')
131         client_block_device['delete_on_termination'] = bool(
132             block_device.get('delete_on_termination', True))
133         client_block_device['uuid'] = block_device['uuid']
134         client_block_device['boot_index'] = int(
135             block_device.get('boot_index', 0))
136         # Allow None here. It will be rendered later.
137         client_block_device['volume_size'] = block_device.get('volume_size')
138         return client_block_device
139 
140     @defer.inlineCallbacks
141     def _renderBlockDevice(self, block_device, build):
142         """Render all of the block device's values."""
143         rendered_block_device = yield build.render(block_device)
144         if rendered_block_device['volume_size'] is None:
145             source_type = rendered_block_device['source_type']
146             source_uuid = rendered_block_device['uuid']
147             volume_size = self._determineVolumeSize(source_type, source_uuid)
148             rendered_block_device['volume_size'] = volume_size
149         defer.returnValue(rendered_block_device)
150 
151     def _determineVolumeSize(self, source_type, source_uuid):
152         """
153         Determine the minimum size the volume needs to be for the source.
154         Returns the size in GiB.
155         """
156         nova = self.novaclient
157         if source_type == 'image':
158             # The size returned for an image is in bytes. Round up to the next
159             # integer GiB.
160             image = nova.images.get(source_uuid)
161             if hasattr(image, 'OS-EXT-IMG-SIZE:size'):
162                 size = getattr(image, 'OS-EXT-IMG-SIZE:size')
163                 size_gb = int(math.ceil(size / 1024.0**3))
164                 return size_gb
165         elif source_type == 'volume':
166             # Volumes are easy because they are already in GiB.
167             volume = nova.volumes.get(source_uuid)
168             return volume.size
169         elif source_type == 'snapshot':
170             snap = nova.volume_snapshots.get(source_uuid)
171             return snap.size
172         else:
173             unknown_source = ("The source type '%s' for UUID '%s' is"
174                               " unknown" % (source_type, source_uuid))
175             raise ValueError(unknown_source)
176 
177     @defer.inlineCallbacks
178     def _getImage(self, build):
179         # If image is a callable, then pass it the list of images. The
180         # function should return the image's UUID to use.
181         image = self.image
182         if callable(image):
183             image_uuid = image(self.novaclient.images.list())
184         else:
185             image_uuid = yield build.render(image)
186         defer.returnValue(image_uuid)
187 
188     @defer.inlineCallbacks
189     def start_instance(self, build):
190         if self.instance is not None:
191             raise ValueError('instance active')
192         image = yield self._getImage(build)
193         if self.block_devices is not None:
194             block_devices = []
195             for bd in self.block_devices:
196                 rendered_block_device = yield self._renderBlockDevice(bd, build)
197                 block_devices.append(rendered_block_device)
198         else:
199             block_devices = None
200         res = yield threads.deferToThread(self._start_instance, image,
201                                           block_devices)
202         defer.returnValue(res)
203 
204     def _start_instance(self, image_uuid, block_devices):
205         boot_args = [self.workername, image_uuid, self.flavor]
206         boot_kwargs = dict(
207             meta=self.meta,
208             block_device_mapping_v2=block_devices,
209             **self.nova_args)
210         instance = self.novaclient.servers.create(*boot_args, **boot_kwargs)
211         # There is an issue when using sessions that the status is not
212         # available on the first try. Trying again will work fine. Fetch the
213         # instance to avoid that.
214         try:
215             instance = self.novaclient.servers.get(instance.id)
216         except NotFound:
217             log.msg('{class_name} {name} instance {instance.id} '
218                     '({instance.name}) never found',
219                     class_name=self.__class__.__name__, name=self.workername,
220                     instance=instance)
221             raise LatentWorkerFailedToSubstantiate(
222                 instance.id, BUILD)
223         self.instance = instance
224         log.msg('%s %s starting instance %s (image %s)' %
225                 (self.__class__.__name__, self.workername, instance.id,
226                  image_uuid))
227         duration = 0
228         interval = self._poll_resolution
229         while instance.status.startswith(BUILD):
230             time.sleep(interval)
231             duration += interval
232             if duration % 60 == 0:
233                 log.msg('%s %s has waited %d minutes for instance %s' %
234                         (self.__class__.__name__, self.workername, duration // 60,
235                          instance.id))
236             try:
237                 instance = self.novaclient.servers.get(instance.id)
238             except NotFound:
239                 log.msg('%s %s instance %s (%s) went missing' %
240                         (self.__class__.__name__, self.workername,
241                          instance.id, instance.name))
242                 raise LatentWorkerFailedToSubstantiate(
243                     instance.id, instance.status)
244         if instance.status == ACTIVE:
245             minutes = duration // 60
246             seconds = duration % 60
247             log.msg('%s %s instance %s (%s) started '
248                     'in about %d minutes %d seconds' %
249                     (self.__class__.__name__, self.workername,
250                      instance.id, instance.name, minutes, seconds))
251             return [instance.id, image_uuid,
252                     '%02d:%02d:%02d' % (minutes // 60, minutes % 60, seconds)]
253         else:
254             self.failed_to_start(instance.id, instance.status)
255 
256     def stop_instance(self, fast=False):
257         if self.instance is None:
258             # be gentle.  Something may just be trying to alert us that an
259             # instance never attached, and it's because, somehow, we never
260             # started.
261             return defer.succeed(None)
262         instance = self.instance
263         self.instance = None
264         self._stop_instance(instance, fast)
265 
266     def _stop_instance(self, instance, fast):
267         try:
268             instance = self.novaclient.servers.get(instance.id)
269         except NotFound:
270             # If can't find the instance, then it's already gone.
271             log.msg('%s %s instance %s (%s) already terminated' %
272                     (self.__class__.__name__, self.workername, instance.id,
273                      instance.name))
274             return
275         if instance.status not in (DELETED, UNKNOWN):
276             instance.delete()
277             log.msg('%s %s terminating instance %s (%s)' %
278                     (self.__class__.__name__, self.workername, instance.id,
279                      instance.name))
280
```
--- 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/master/buildbot/worker/openstack.py b/master/buildbot/worker/openstack.py
--- a/master/buildbot/worker/openstack.py
+++ b/master/buildbot/worker/openstack.py
@@ -60,6 +60,7 @@
                  os_tenant_name,
                  os_auth_url,
                  block_devices=None,
+                 region=None,
                  image=None,
                  meta=None,
                  # Have a nova_args parameter to allow passing things directly
@@ -88,6 +89,8 @@
             self.novaclient = self._constructClient(
                 client_version, os_username, os_password, os_tenant_name,
                 os_auth_url)
+            if region is not None:
+                self.novaclient.client.region_name = region
 
         if block_devices is not None:
             self.block_devices = [

{"golden_diff": "diff --git a/master/buildbot/worker/openstack.py b/master/buildbot/worker/openstack.py\n--- a/master/buildbot/worker/openstack.py\n+++ b/master/buildbot/worker/openstack.py\n@@ -60,6 +60,7 @@\n                  os_tenant_name,\n                  os_auth_url,\n                  block_devices=None,\n+                 region=None,\n                  image=None,\n                  meta=None,\n                  # Have a nova_args parameter to allow passing things directly\n@@ -88,6 +89,8 @@\n             self.novaclient = self._constructClient(\n                 client_version, os_username, os_password, os_tenant_name,\n                 os_auth_url)\n+            if region is not None:\n+                self.novaclient.client.region_name = region\n \n         if block_devices is not None:\n             self.block_devices = [\n", "issue": "Add a `region` arg to `OpenStackLatentWorker`\nWould be nice to allow passing a region_name to novaclient in `OpenStackLatentWorker`.\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Portions Copyright Buildbot Team Members\n# Portions Copyright 2013 Cray Inc.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport math\nimport time\n\nfrom twisted.internet import defer\nfrom twisted.internet import threads\nfrom twisted.python import log\n\nfrom buildbot import config\nfrom buildbot.interfaces import LatentWorkerFailedToSubstantiate\nfrom buildbot.worker import AbstractLatentWorker\n\ntry:\n    from keystoneauth1 import loading\n    from keystoneauth1 import session\n    from novaclient import client\n    from novaclient.exceptions import NotFound\n    _hush_pyflakes = [client]\nexcept ImportError:\n    NotFound = Exception\n    client = None\n    loading = None\n    session = None\n\n\nACTIVE = 'ACTIVE'\nBUILD = 'BUILD'\nDELETED = 'DELETED'\nUNKNOWN = 'UNKNOWN'\n\n\nclass OpenStackLatentWorker(AbstractLatentWorker):\n\n    instance = None\n    _poll_resolution = 5  # hook point for tests\n\n    def __init__(self, name, password,\n                 flavor,\n                 os_username,\n                 os_password,\n                 os_tenant_name,\n                 os_auth_url,\n                 block_devices=None,\n                 image=None,\n                 meta=None,\n                 # Have a nova_args parameter to allow passing things directly\n                 # to novaclient.\n                 nova_args=None,\n                 client_version='2',\n                 **kwargs):\n\n        if not client:\n            config.error(\"The python module 'novaclient' is needed  \"\n                         \"to use a OpenStackLatentWorker. \"\n                         \"Please install 'python-novaclient' package.\")\n        if not loading or not session:\n            config.error(\"The python module 'keystoneauth1' is needed \"\n                         \"to use a OpenStackLatentWorker. \"\n                         \"Please install the 'keystoneauth1' package.\")\n\n        if not block_devices and not image:\n            raise ValueError('One of block_devices or image must be given')\n\n        AbstractLatentWorker.__init__(self, name, password, **kwargs)\n\n        self.flavor = flavor\n        self.client_version = client_version\n        if client:\n            self.novaclient = self._constructClient(\n                client_version, os_username, os_password, os_tenant_name,\n                os_auth_url)\n\n        if block_devices is not None:\n            self.block_devices = [\n                self._parseBlockDevice(bd) for bd in block_devices]\n        else:\n            self.block_devices = None\n        self.image = image\n        self.meta = meta\n        self.nova_args = nova_args if nova_args is not None else {}\n\n    @staticmethod\n    def _constructClient(client_version, username, password, project_name,\n                         auth_url):\n        \"\"\"Return a novaclient from the given args.\"\"\"\n        loader = loading.get_plugin_loader('password')\n        auth = loader.load_from_options(auth_url=auth_url, username=username,\n                                        password=password, project_name=project_name)\n        sess = session.Session(auth=auth)\n        return client.Client(client_version, session=sess)\n\n    def _parseBlockDevice(self, block_device):\n        \"\"\"\n        Parse a higher-level view of the block device mapping into something\n        novaclient wants. This should be similar to how Horizon presents it.\n        Required keys:\n            device_name: The name of the device; e.g. vda or xda.\n            source_type: image, snapshot, volume, or blank/None.\n            destination_type: Destination of block device: volume or local.\n            delete_on_termination: True/False.\n            uuid: The image, snapshot, or volume id.\n            boot_index: Integer used for boot order.\n            volume_size: Size of the device in GiB.\n        \"\"\"\n        client_block_device = {}\n        client_block_device['device_name'] = block_device.get(\n            'device_name', 'vda')\n        client_block_device['source_type'] = block_device.get(\n            'source_type', 'image')\n        client_block_device['destination_type'] = block_device.get(\n            'destination_type', 'volume')\n        client_block_device['delete_on_termination'] = bool(\n            block_device.get('delete_on_termination', True))\n        client_block_device['uuid'] = block_device['uuid']\n        client_block_device['boot_index'] = int(\n            block_device.get('boot_index', 0))\n        # Allow None here. It will be rendered later.\n        client_block_device['volume_size'] = block_device.get('volume_size')\n        return client_block_device\n\n    @defer.inlineCallbacks\n    def _renderBlockDevice(self, block_device, build):\n        \"\"\"Render all of the block device's values.\"\"\"\n        rendered_block_device = yield build.render(block_device)\n        if rendered_block_device['volume_size'] is None:\n            source_type = rendered_block_device['source_type']\n            source_uuid = rendered_block_device['uuid']\n            volume_size = self._determineVolumeSize(source_type, source_uuid)\n            rendered_block_device['volume_size'] = volume_size\n        defer.returnValue(rendered_block_device)\n\n    def _determineVolumeSize(self, source_type, source_uuid):\n        \"\"\"\n        Determine the minimum size the volume needs to be for the source.\n        Returns the size in GiB.\n        \"\"\"\n        nova = self.novaclient\n        if source_type == 'image':\n            # The size returned for an image is in bytes. Round up to the next\n            # integer GiB.\n            image = nova.images.get(source_uuid)\n            if hasattr(image, 'OS-EXT-IMG-SIZE:size'):\n                size = getattr(image, 'OS-EXT-IMG-SIZE:size')\n                size_gb = int(math.ceil(size / 1024.0**3))\n                return size_gb\n        elif source_type == 'volume':\n            # Volumes are easy because they are already in GiB.\n            volume = nova.volumes.get(source_uuid)\n            return volume.size\n        elif source_type == 'snapshot':\n            snap = nova.volume_snapshots.get(source_uuid)\n            return snap.size\n        else:\n            unknown_source = (\"The source type '%s' for UUID '%s' is\"\n                              \" unknown\" % (source_type, source_uuid))\n            raise ValueError(unknown_source)\n\n    @defer.inlineCallbacks\n    def _getImage(self, build):\n        # If image is a callable, then pass it the list of images. The\n        # function should return the image's UUID to use.\n        image = self.image\n        if callable(image):\n            image_uuid = image(self.novaclient.images.list())\n        else:\n            image_uuid = yield build.render(image)\n        defer.returnValue(image_uuid)\n\n    @defer.inlineCallbacks\n    def start_instance(self, build):\n        if self.instance is not None:\n            raise ValueError('instance active')\n        image = yield self._getImage(build)\n        if self.block_devices is not None:\n            block_devices = []\n            for bd in self.block_devices:\n                rendered_block_device = yield self._renderBlockDevice(bd, build)\n                block_devices.append(rendered_block_device)\n        else:\n            block_devices = None\n        res = yield threads.deferToThread(self._start_instance, image,\n                                          block_devices)\n        defer.returnValue(res)\n\n    def _start_instance(self, image_uuid, block_devices):\n        boot_args = [self.workername, image_uuid, self.flavor]\n        boot_kwargs = dict(\n            meta=self.meta,\n            block_device_mapping_v2=block_devices,\n            **self.nova_args)\n        instance = self.novaclient.servers.create(*boot_args, **boot_kwargs)\n        # There is an issue when using sessions that the status is not\n        # available on the first try. Trying again will work fine. Fetch the\n        # instance to avoid that.\n        try:\n            instance = self.novaclient.servers.get(instance.id)\n        except NotFound:\n            log.msg('{class_name} {name} instance {instance.id} '\n                    '({instance.name}) never found',\n                    class_name=self.__class__.__name__, name=self.workername,\n                    instance=instance)\n            raise LatentWorkerFailedToSubstantiate(\n                instance.id, BUILD)\n        self.instance = instance\n        log.msg('%s %s starting instance %s (image %s)' %\n                (self.__class__.__name__, self.workername, instance.id,\n                 image_uuid))\n        duration = 0\n        interval = self._poll_resolution\n        while instance.status.startswith(BUILD):\n            time.sleep(interval)\n            duration += interval\n            if duration % 60 == 0:\n                log.msg('%s %s has waited %d minutes for instance %s' %\n                        (self.__class__.__name__, self.workername, duration // 60,\n                         instance.id))\n            try:\n                instance = self.novaclient.servers.get(instance.id)\n            except NotFound:\n                log.msg('%s %s instance %s (%s) went missing' %\n                        (self.__class__.__name__, self.workername,\n                         instance.id, instance.name))\n                raise LatentWorkerFailedToSubstantiate(\n                    instance.id, instance.status)\n        if instance.status == ACTIVE:\n            minutes = duration // 60\n            seconds = duration % 60\n            log.msg('%s %s instance %s (%s) started '\n                    'in about %d minutes %d seconds' %\n                    (self.__class__.__name__, self.workername,\n                     instance.id, instance.name, minutes, seconds))\n            return [instance.id, image_uuid,\n                    '%02d:%02d:%02d' % (minutes // 60, minutes % 60, seconds)]\n        else:\n            self.failed_to_start(instance.id, instance.status)\n\n    def stop_instance(self, fast=False):\n        if self.instance is None:\n            # be gentle.  Something may just be trying to alert us that an\n            # instance never attached, and it's because, somehow, we never\n            # started.\n            return defer.succeed(None)\n        instance = self.instance\n        self.instance = None\n        self._stop_instance(instance, fast)\n\n    def _stop_instance(self, instance, fast):\n        try:\n            instance = self.novaclient.servers.get(instance.id)\n        except NotFound:\n            # If can't find the instance, then it's already gone.\n            log.msg('%s %s instance %s (%s) already terminated' %\n                    (self.__class__.__name__, self.workername, instance.id,\n                     instance.name))\n            return\n        if instance.status not in (DELETED, UNKNOWN):\n            instance.delete()\n            log.msg('%s %s terminating instance %s (%s)' %\n                    (self.__class__.__name__, self.workername, instance.id,\n                     instance.name))\n", "path": "master/buildbot/worker/openstack.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Portions Copyright Buildbot Team Members\n# Portions Copyright 2013 Cray Inc.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport math\nimport time\n\nfrom twisted.internet import defer\nfrom twisted.internet import threads\nfrom twisted.python import log\n\nfrom buildbot import config\nfrom buildbot.interfaces import LatentWorkerFailedToSubstantiate\nfrom buildbot.worker import AbstractLatentWorker\n\ntry:\n    from keystoneauth1 import loading\n    from keystoneauth1 import session\n    from novaclient import client\n    from novaclient.exceptions import NotFound\n    _hush_pyflakes = [client]\nexcept ImportError:\n    NotFound = Exception\n    client = None\n    loading = None\n    session = None\n\n\nACTIVE = 'ACTIVE'\nBUILD = 'BUILD'\nDELETED = 'DELETED'\nUNKNOWN = 'UNKNOWN'\n\n\nclass OpenStackLatentWorker(AbstractLatentWorker):\n\n    instance = None\n    _poll_resolution = 5  # hook point for tests\n\n    def __init__(self, name, password,\n                 flavor,\n                 os_username,\n                 os_password,\n                 os_tenant_name,\n                 os_auth_url,\n                 block_devices=None,\n                 region=None,\n                 image=None,\n                 meta=None,\n                 # Have a nova_args parameter to allow passing things directly\n                 # to novaclient.\n                 nova_args=None,\n                 client_version='2',\n                 **kwargs):\n\n        if not client:\n            config.error(\"The python module 'novaclient' is needed  \"\n                         \"to use a OpenStackLatentWorker. \"\n                         \"Please install 'python-novaclient' package.\")\n        if not loading or not session:\n            config.error(\"The python module 'keystoneauth1' is needed \"\n                         \"to use a OpenStackLatentWorker. \"\n                         \"Please install the 'keystoneauth1' package.\")\n\n        if not block_devices and not image:\n            raise ValueError('One of block_devices or image must be given')\n\n        AbstractLatentWorker.__init__(self, name, password, **kwargs)\n\n        self.flavor = flavor\n        self.client_version = client_version\n        if client:\n            self.novaclient = self._constructClient(\n                client_version, os_username, os_password, os_tenant_name,\n                os_auth_url)\n            if region is not None:\n                self.novaclient.client.region_name = region\n\n        if block_devices is not None:\n            self.block_devices = [\n                self._parseBlockDevice(bd) for bd in block_devices]\n        else:\n            self.block_devices = None\n        self.image = image\n        self.meta = meta\n        self.nova_args = nova_args if nova_args is not None else {}\n\n    @staticmethod\n    def _constructClient(client_version, username, password, project_name,\n                         auth_url):\n        \"\"\"Return a novaclient from the given args.\"\"\"\n        loader = loading.get_plugin_loader('password')\n        auth = loader.load_from_options(auth_url=auth_url, username=username,\n                                        password=password, project_name=project_name)\n        sess = session.Session(auth=auth)\n        return client.Client(client_version, session=sess)\n\n    def _parseBlockDevice(self, block_device):\n        \"\"\"\n        Parse a higher-level view of the block device mapping into something\n        novaclient wants. This should be similar to how Horizon presents it.\n        Required keys:\n            device_name: The name of the device; e.g. vda or xda.\n            source_type: image, snapshot, volume, or blank/None.\n            destination_type: Destination of block device: volume or local.\n            delete_on_termination: True/False.\n            uuid: The image, snapshot, or volume id.\n            boot_index: Integer used for boot order.\n            volume_size: Size of the device in GiB.\n        \"\"\"\n        client_block_device = {}\n        client_block_device['device_name'] = block_device.get(\n            'device_name', 'vda')\n        client_block_device['source_type'] = block_device.get(\n            'source_type', 'image')\n        client_block_device['destination_type'] = block_device.get(\n            'destination_type', 'volume')\n        client_block_device['delete_on_termination'] = bool(\n            block_device.get('delete_on_termination', True))\n        client_block_device['uuid'] = block_device['uuid']\n        client_block_device['boot_index'] = int(\n            block_device.get('boot_index', 0))\n        # Allow None here. It will be rendered later.\n        client_block_device['volume_size'] = block_device.get('volume_size')\n        return client_block_device\n\n    @defer.inlineCallbacks\n    def _renderBlockDevice(self, block_device, build):\n        \"\"\"Render all of the block device's values.\"\"\"\n        rendered_block_device = yield build.render(block_device)\n        if rendered_block_device['volume_size'] is None:\n            source_type = rendered_block_device['source_type']\n            source_uuid = rendered_block_device['uuid']\n            volume_size = self._determineVolumeSize(source_type, source_uuid)\n            rendered_block_device['volume_size'] = volume_size\n        defer.returnValue(rendered_block_device)\n\n    def _determineVolumeSize(self, source_type, source_uuid):\n        \"\"\"\n        Determine the minimum size the volume needs to be for the source.\n        Returns the size in GiB.\n        \"\"\"\n        nova = self.novaclient\n        if source_type == 'image':\n            # The size returned for an image is in bytes. Round up to the next\n            # integer GiB.\n            image = nova.images.get(source_uuid)\n            if hasattr(image, 'OS-EXT-IMG-SIZE:size'):\n                size = getattr(image, 'OS-EXT-IMG-SIZE:size')\n                size_gb = int(math.ceil(size / 1024.0**3))\n                return size_gb\n        elif source_type == 'volume':\n            # Volumes are easy because they are already in GiB.\n            volume = nova.volumes.get(source_uuid)\n            return volume.size\n        elif source_type == 'snapshot':\n            snap = nova.volume_snapshots.get(source_uuid)\n            return snap.size\n        else:\n            unknown_source = (\"The source type '%s' for UUID '%s' is\"\n                              \" unknown\" % (source_type, source_uuid))\n            raise ValueError(unknown_source)\n\n    @defer.inlineCallbacks\n    def _getImage(self, build):\n        # If image is a callable, then pass it the list of images. The\n        # function should return the image's UUID to use.\n        image = self.image\n        if callable(image):\n            image_uuid = image(self.novaclient.images.list())\n        else:\n            image_uuid = yield build.render(image)\n        defer.returnValue(image_uuid)\n\n    @defer.inlineCallbacks\n    def start_instance(self, build):\n        if self.instance is not None:\n            raise ValueError('instance active')\n        image = yield self._getImage(build)\n        if self.block_devices is not None:\n            block_devices = []\n            for bd in self.block_devices:\n                rendered_block_device = yield self._renderBlockDevice(bd, build)\n                block_devices.append(rendered_block_device)\n        else:\n            block_devices = None\n        res = yield threads.deferToThread(self._start_instance, image,\n                                          block_devices)\n        defer.returnValue(res)\n\n    def _start_instance(self, image_uuid, block_devices):\n        boot_args = [self.workername, image_uuid, self.flavor]\n        boot_kwargs = dict(\n            meta=self.meta,\n            block_device_mapping_v2=block_devices,\n            **self.nova_args)\n        instance = self.novaclient.servers.create(*boot_args, **boot_kwargs)\n        # There is an issue when using sessions that the status is not\n        # available on the first try. Trying again will work fine. Fetch the\n        # instance to avoid that.\n        try:\n            instance = self.novaclient.servers.get(instance.id)\n        except NotFound:\n            log.msg('{class_name} {name} instance {instance.id} '\n                    '({instance.name}) never found',\n                    class_name=self.__class__.__name__, name=self.workername,\n                    instance=instance)\n            raise LatentWorkerFailedToSubstantiate(\n                instance.id, BUILD)\n        self.instance = instance\n        log.msg('%s %s starting instance %s (image %s)' %\n                (self.__class__.__name__, self.workername, instance.id,\n                 image_uuid))\n        duration = 0\n        interval = self._poll_resolution\n        while instance.status.startswith(BUILD):\n            time.sleep(interval)\n            duration += interval\n            if duration % 60 == 0:\n                log.msg('%s %s has waited %d minutes for instance %s' %\n                        (self.__class__.__name__, self.workername, duration // 60,\n                         instance.id))\n            try:\n                instance = self.novaclient.servers.get(instance.id)\n            except NotFound:\n                log.msg('%s %s instance %s (%s) went missing' %\n                        (self.__class__.__name__, self.workername,\n                         instance.id, instance.name))\n                raise LatentWorkerFailedToSubstantiate(\n                    instance.id, instance.status)\n        if instance.status == ACTIVE:\n            minutes = duration // 60\n            seconds = duration % 60\n            log.msg('%s %s instance %s (%s) started '\n                    'in about %d minutes %d seconds' %\n                    (self.__class__.__name__, self.workername,\n                     instance.id, instance.name, minutes, seconds))\n            return [instance.id, image_uuid,\n                    '%02d:%02d:%02d' % (minutes // 60, minutes % 60, seconds)]\n        else:\n            self.failed_to_start(instance.id, instance.status)\n\n    def stop_instance(self, fast=False):\n        if self.instance is None:\n            # be gentle.  Something may just be trying to alert us that an\n            # instance never attached, and it's because, somehow, we never\n            # started.\n            return defer.succeed(None)\n        instance = self.instance\n        self.instance = None\n        self._stop_instance(instance, fast)\n\n    def _stop_instance(self, instance, fast):\n        try:\n            instance = self.novaclient.servers.get(instance.id)\n        except NotFound:\n            # If can't find the instance, then it's already gone.\n            log.msg('%s %s instance %s (%s) already terminated' %\n                    (self.__class__.__name__, self.workername, instance.id,\n                     instance.name))\n            return\n        if instance.status not in (DELETED, UNKNOWN):\n            instance.delete()\n            log.msg('%s %s terminating instance %s (%s)' %\n                    (self.__class__.__name__, self.workername, instance.id,\n                     instance.name))\n", "path": "master/buildbot/worker/openstack.py"}]}