Browse Source

Support creating a swap partition

BREAKING: --root-disk-size => --root-size

Story: #2002171
Task: #20033
Change-Id: I5d6b38e1a9e71b608b40e140e34c4509b896a1ff
Dmitry Tantsur 7 months ago
parent
commit
2afd20938d

+ 1
- 0
.zuul.yaml View File

@@ -124,6 +124,7 @@
124 124
       metalsmith_precreate_port: false
125 125
       metalsmith_partition_image: test-centos-partition
126 126
       metalsmith_whole_disk_image: test-centos-wholedisk
127
+      metalsmith_swap_size: 1024
127 128
       metalsmith_traits: [CUSTOM_GOLD]
128 129
 
129 130
 - job:

+ 8
- 4
metalsmith/_cmd.py View File

@@ -81,7 +81,8 @@ def _do_deploy(api, args, formatter):
81 81
     instance = api.provision_node(node,
82 82
                                   image=source,
83 83
                                   nics=args.nics,
84
-                                  root_disk_size=args.root_disk_size,
84
+                                  root_size_gb=args.root_size,
85
+                                  swap_size_mb=args.swap_size,
85 86
                                   config=config,
86 87
                                   hostname=args.hostname,
87 88
                                   netboot=args.netboot,
@@ -150,9 +151,12 @@ def _parse_args(args, config):
150 151
                         dest='nics', action=NICAction)
151 152
     deploy.add_argument('--netboot', action='store_true',
152 153
                         help='boot from network instead of local disk')
153
-    deploy.add_argument('--root-disk-size', type=int,
154
-                        help='root disk size (in GiB), defaults to (local_gb '
155
-                        '- 2)')
154
+    deploy.add_argument('--root-size', type=int,
155
+                        help='root partition size (in GiB), defaults to '
156
+                        '(local_gb - 1)')
157
+    deploy.add_argument('--swap-size', type=int,
158
+                        help='swap partition size (in MiB), defaults to '
159
+                        'no swap')
156 160
     deploy.add_argument('--capability', action='append', metavar='NAME=VALUE',
157 161
                         default=[], help='capabilities the node should have')
158 162
     deploy.add_argument('--trait', action='append',

+ 18
- 7
metalsmith/_provisioner.py View File

@@ -17,6 +17,7 @@ import logging
17 17
 import random
18 18
 import sys
19 19
 import time
20
+import warnings
20 21
 
21 22
 from openstack import connection
22 23
 import six
@@ -194,9 +195,10 @@ class Provisioner(object):
194 195
 
195 196
         return hostname
196 197
 
197
-    def provision_node(self, node, image, nics=None, root_disk_size=None,
198
-                       config=None, hostname=None, netboot=False,
199
-                       capabilities=None, traits=None, wait=None):
198
+    def provision_node(self, node, image, nics=None, root_size_gb=None,
199
+                       swap_size_mb=None, config=None, hostname=None,
200
+                       netboot=False, capabilities=None, traits=None,
201
+                       wait=None, root_disk_size=None):
200 202
         """Provision the node with the given image.
201 203
 
202 204
         Example::
@@ -204,7 +206,7 @@ class Provisioner(object):
204 206
          provisioner.provision_node("compute-1", "centos",
205 207
                                     nics=[{"network": "private"},
206 208
                                           {"network": "external"}],
207
-                                    root_disk_size=50,
209
+                                    root_size_gb=50,
208 210
                                     wait=3600)
209 211
 
210 212
         :param node: Node object, UUID or name. Will be reserved first, if
@@ -216,8 +218,10 @@ class Provisioner(object):
216 218
             Each item is a dict with a key describing the type of the NIC:
217 219
             either a port (``{"port": "<port name or ID>"}``) or a network
218 220
             to create a port on (``{"network": "<network name or ID>"}``).
219
-        :param root_disk_size: The size of the root partition. By default
221
+        :param root_size_gb: The size of the root partition. By default
220 222
             the value of the local_gb property is used.
223
+        :param swap_size_mb: The size of the swap partition. It's an error
224
+            to specify it for a whole disk image.
221 225
         :param config: :py:class:`metalsmith.InstanceConfig` object with
222 226
             the configuration to pass to the instance.
223 227
         :param hostname: Hostname to assign to the instance. Defaults to the
@@ -233,6 +237,7 @@ class Provisioner(object):
233 237
             :meth:`reserve_node` for that.
234 238
         :param wait: How many seconds to wait for the deployment to finish,
235 239
             None to return immediately.
240
+        :param root_disk_size: DEPRECATED, use ``root_size_gb``.
236 241
         :return: :py:class:`metalsmith.Instance` object with the current
237 242
             status of provisioning. If ``wait`` is not ``None``, provisioning
238 243
             is already finished.
@@ -242,6 +247,10 @@ class Provisioner(object):
242 247
             config = _config.InstanceConfig()
243 248
         if isinstance(image, six.string_types):
244 249
             image = sources.GlanceImage(image)
250
+        if root_disk_size is not None:
251
+            warnings.warn("root_disk_size is deprecated, use root_size_gb "
252
+                          "instead", DeprecationWarning)
253
+            root_size_gb = root_disk_size
245 254
 
246 255
         node = self._check_node_for_deploy(node)
247 256
         created_ports = []
@@ -249,7 +258,7 @@ class Provisioner(object):
249 258
 
250 259
         try:
251 260
             hostname = self._check_hostname(node, hostname)
252
-            root_disk_size = _utils.get_root_disk(root_disk_size, node)
261
+            root_size_gb = _utils.get_root_disk(root_size_gb, node)
253 262
 
254 263
             image._validate(self.connection)
255 264
 
@@ -268,7 +277,7 @@ class Provisioner(object):
268 277
 
269 278
             capabilities['boot_option'] = 'netboot' if netboot else 'local'
270 279
 
271
-            updates = {'/instance_info/root_gb': root_disk_size,
280
+            updates = {'/instance_info/root_gb': root_size_gb,
272 281
                        '/instance_info/capabilities': capabilities,
273 282
                        '/extra/%s' % _CREATED_PORTS: created_ports,
274 283
                        '/extra/%s' % _ATTACHED_PORTS: attached_ports,
@@ -276,6 +285,8 @@ class Provisioner(object):
276 285
             updates.update(image._node_updates(self.connection))
277 286
             if traits is not None:
278 287
                 updates['/instance_info/traits'] = traits
288
+            if swap_size_mb is not None:
289
+                updates['/instance_info/swap_mb'] = swap_size_mb
279 290
 
280 291
             LOG.debug('Updating node %(node)s with %(updates)s',
281 292
                       {'node': _utils.log_node(node), 'updates': updates})

+ 12
- 12
metalsmith/_utils.py View File

@@ -42,22 +42,22 @@ def get_capabilities(node):
42 42
     return caps
43 43
 
44 44
 
45
-def get_root_disk(root_disk_size, node):
45
+def get_root_disk(root_size_gb, node):
46 46
     """Validate and calculate the root disk size."""
47
-    if root_disk_size is not None:
48
-        if not isinstance(root_disk_size, int):
49
-            raise TypeError("The root_disk_size argument must be "
50
-                            "a positive integer, got %r" % root_disk_size)
51
-        elif root_disk_size <= 0:
52
-            raise ValueError("The root_disk_size argument must be "
53
-                             "a positive integer, got %d" % root_disk_size)
47
+    if root_size_gb is not None:
48
+        if not isinstance(root_size_gb, int):
49
+            raise TypeError("The root_size_gb argument must be "
50
+                            "a positive integer, got %r" % root_size_gb)
51
+        elif root_size_gb <= 0:
52
+            raise ValueError("The root_size_gb argument must be "
53
+                             "a positive integer, got %d" % root_size_gb)
54 54
     else:
55 55
         try:
56 56
             assert int(node.properties['local_gb']) > 0
57 57
         except KeyError:
58 58
             raise exceptions.UnknownRootDiskSize(
59
-                'No local_gb for node %s and no root disk size requested' %
60
-                log_node(node))
59
+                'No local_gb for node %s and no root partition size '
60
+                'specified' % log_node(node))
61 61
         except (TypeError, ValueError, AssertionError):
62 62
             raise exceptions.UnknownRootDiskSize(
63 63
                 'The local_gb for node %(node)s is invalid: '
@@ -66,9 +66,9 @@ def get_root_disk(root_disk_size, node):
66 66
                  'value': node.properties['local_gb']})
67 67
 
68 68
         # allow for partitioning and config drive
69
-        root_disk_size = int(node.properties['local_gb']) - 1
69
+        root_size_gb = int(node.properties['local_gb']) - 1
70 70
 
71
-    return root_disk_size
71
+    return root_size_gb
72 72
 
73 73
 
74 74
 _HOSTNAME_RE = re.compile(r"""^

+ 126
- 513
metalsmith/test/test_cmd.py View File

@@ -29,7 +29,6 @@ from metalsmith import sources
29 29
 
30 30
 
31 31
 @mock.patch.object(_provisioner, 'Provisioner', autospec=True)
32
-@mock.patch.object(_cmd.os_config, 'OpenStackConfig', autospec=True)
33 32
 class TestDeploy(testtools.TestCase):
34 33
     def setUp(self):
35 34
         super(TestDeploy, self).setUp()
@@ -37,8 +36,42 @@ class TestDeploy(testtools.TestCase):
37 36
             'metalsmith._format._print', autospec=True))
38 37
         self.mock_print = self.print_fixture.mock
39 38
 
39
+        self.os_conf_fixture = self.useFixture(fixtures.MockPatchObject(
40
+            _cmd.os_config, 'OpenStackConfig', autospec=True))
41
+        self.mock_os_conf = self.os_conf_fixture.mock
42
+
43
+    def _check(self, mock_pr, args, reserve_args, provision_args,
44
+               dry_run=False):
45
+        reserve_defaults = dict(resource_class='compute',
46
+                                conductor_group=None,
47
+                                capabilities={},
48
+                                traits=[],
49
+                                candidates=None)
50
+        reserve_defaults.update(reserve_args)
51
+
52
+        provision_defaults = dict(image='myimg',
53
+                                  nics=[{'network': 'mynet'}],
54
+                                  root_size_gb=None,
55
+                                  swap_size_mb=None,
56
+                                  config=mock.ANY,
57
+                                  hostname=None,
58
+                                  netboot=False,
59
+                                  wait=1800)
60
+        provision_defaults.update(provision_args)
61
+
62
+        _cmd.main(args)
63
+
64
+        mock_pr.assert_called_once_with(
65
+            cloud_region=self.mock_os_conf.return_value.get_one.return_value,
66
+            dry_run=dry_run)
67
+        mock_pr.return_value.reserve_node.assert_called_once_with(
68
+            **reserve_defaults)
69
+        mock_pr.return_value.provision_node.assert_called_once_with(
70
+            mock_pr.return_value.reserve_node.return_value,
71
+            **provision_defaults)
72
+
40 73
     @mock.patch.object(_cmd, 'logging', autospec=True)
41
-    def test_args_ok(self, mock_log, mock_os_conf, mock_pr):
74
+    def test_args_ok(self, mock_log, mock_pr):
42 75
         instance = mock_pr.return_value.provision_node.return_value
43 76
         instance.create_autospec(_instance.Instance)
44 77
         instance.node.name = None
@@ -49,27 +82,8 @@ class TestDeploy(testtools.TestCase):
49 82
 
50 83
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
51 84
                 '--resource-class', 'compute']
52
-        _cmd.main(args)
85
+        self._check(mock_pr, args, {}, {})
53 86
 
54
-        mock_pr.assert_called_once_with(
55
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
56
-            dry_run=False)
57
-        mock_pr.return_value.reserve_node.assert_called_once_with(
58
-            resource_class='compute',
59
-            conductor_group=None,
60
-            capabilities={},
61
-            traits=[],
62
-            candidates=None
63
-        )
64
-        mock_pr.return_value.provision_node.assert_called_once_with(
65
-            mock_pr.return_value.reserve_node.return_value,
66
-            image='myimg',
67
-            nics=[{'network': 'mynet'}],
68
-            root_disk_size=None,
69
-            config=mock.ANY,
70
-            hostname=None,
71
-            netboot=False,
72
-            wait=1800)
73 87
         config = mock_pr.return_value.provision_node.call_args[1]['config']
74 88
         self.assertEqual([], config.ssh_keys)
75 89
         mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
@@ -86,7 +100,7 @@ class TestDeploy(testtools.TestCase):
86 100
         ])
87 101
 
88 102
     @mock.patch.object(_cmd, 'logging', autospec=True)
89
-    def test_args_json_format(self, mock_log, mock_os_conf, mock_pr):
103
+    def test_args_json_format(self, mock_log, mock_pr):
90 104
         instance = mock_pr.return_value.provision_node.return_value
91 105
         instance.create_autospec(_instance.Instance)
92 106
         instance.to_dict.return_value = {'node': 'dict'}
@@ -95,29 +109,10 @@ class TestDeploy(testtools.TestCase):
95 109
                 '--image', 'myimg', '--resource-class', 'compute']
96 110
         fake_io = six.StringIO()
97 111
         with mock.patch('sys.stdout', fake_io):
98
-            _cmd.main(args)
112
+            self._check(mock_pr, args, {}, {})
99 113
             self.assertEqual(json.loads(fake_io.getvalue()),
100 114
                              {'node': 'dict'})
101 115
 
102
-        mock_pr.assert_called_once_with(
103
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
104
-            dry_run=False)
105
-        mock_pr.return_value.reserve_node.assert_called_once_with(
106
-            resource_class='compute',
107
-            conductor_group=None,
108
-            capabilities={},
109
-            traits=[],
110
-            candidates=None
111
-        )
112
-        mock_pr.return_value.provision_node.assert_called_once_with(
113
-            mock_pr.return_value.reserve_node.return_value,
114
-            image='myimg',
115
-            nics=[{'network': 'mynet'}],
116
-            root_disk_size=None,
117
-            config=mock.ANY,
118
-            hostname=None,
119
-            netboot=False,
120
-            wait=1800)
121 116
         mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
122 117
                                                      format=mock.ANY)
123 118
         self.assertEqual(
@@ -126,7 +121,7 @@ class TestDeploy(testtools.TestCase):
126 121
                 mock_log.CRITICAL).call_list(),
127 122
             mock_log.getLogger.mock_calls)
128 123
 
129
-    def test_no_ips(self, mock_os_conf, mock_pr):
124
+    def test_no_ips(self, mock_pr):
130 125
         instance = mock_pr.return_value.provision_node.return_value
131 126
         instance.create_autospec(_instance.Instance)
132 127
         instance.is_deployed = True
@@ -137,12 +132,12 @@ class TestDeploy(testtools.TestCase):
137 132
 
138 133
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
139 134
                 '--resource-class', 'compute']
140
-        _cmd.main(args)
135
+        self._check(mock_pr, args, {}, {})
141 136
 
142 137
         self.mock_print.assert_called_once_with(mock.ANY, node='123',
143 138
                                                 state='active'),
144 139
 
145
-    def test_not_deployed_no_ips(self, mock_os_conf, mock_pr):
140
+    def test_not_deployed_no_ips(self, mock_pr):
146 141
         instance = mock_pr.return_value.provision_node.return_value
147 142
         instance.create_autospec(_instance.Instance)
148 143
         instance.is_deployed = False
@@ -152,72 +147,34 @@ class TestDeploy(testtools.TestCase):
152 147
 
153 148
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
154 149
                 '--resource-class', 'compute']
155
-        _cmd.main(args)
150
+        self._check(mock_pr, args, {}, {})
156 151
 
157 152
         self.mock_print.assert_called_once_with(mock.ANY, node='123',
158 153
                                                 state='deploying'),
159 154
 
160 155
     @mock.patch.object(_cmd.LOG, 'info', autospec=True)
161
-    def test_no_logs_not_deployed(self, mock_log, mock_os_conf, mock_pr):
156
+    def test_no_logs_not_deployed(self, mock_log, mock_pr):
162 157
         instance = mock_pr.return_value.provision_node.return_value
163 158
         instance.create_autospec(_instance.Instance)
164 159
         instance.is_deployed = False
165 160
 
166 161
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
167 162
                 '--resource-class', 'compute']
168
-        _cmd.main(args)
163
+        self._check(mock_pr, args, {}, {})
169 164
 
170 165
         self.assertFalse(mock_log.called)
171 166
         self.assertFalse(instance.ip_addresses.called)
172 167
 
173
-    def test_args_dry_run(self, mock_os_conf, mock_pr):
168
+    def test_args_dry_run(self, mock_pr):
174 169
         args = ['--dry-run', 'deploy', '--network', 'mynet',
175 170
                 '--image', 'myimg', '--resource-class', 'compute']
176
-        _cmd.main(args)
177
-        mock_pr.assert_called_once_with(
178
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
179
-            dry_run=True)
180
-        mock_pr.return_value.reserve_node.assert_called_once_with(
181
-            resource_class='compute',
182
-            conductor_group=None,
183
-            capabilities={},
184
-            traits=[],
185
-            candidates=None
186
-        )
187
-        mock_pr.return_value.provision_node.assert_called_once_with(
188
-            mock_pr.return_value.reserve_node.return_value,
189
-            image='myimg',
190
-            nics=[{'network': 'mynet'}],
191
-            root_disk_size=None,
192
-            config=mock.ANY,
193
-            hostname=None,
194
-            netboot=False,
195
-            wait=1800)
171
+        self._check(mock_pr, args, {}, {}, dry_run=True)
196 172
 
197 173
     @mock.patch.object(_cmd, 'logging', autospec=True)
198
-    def test_args_debug(self, mock_log, mock_os_conf, mock_pr):
174
+    def test_args_debug(self, mock_log, mock_pr):
199 175
         args = ['--debug', 'deploy', '--network', 'mynet', '--image', 'myimg',
200 176
                 '--resource-class', 'compute']
201
-        _cmd.main(args)
202
-        mock_pr.assert_called_once_with(
203
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
204
-            dry_run=False)
205
-        mock_pr.return_value.reserve_node.assert_called_once_with(
206
-            resource_class='compute',
207
-            conductor_group=None,
208
-            capabilities={},
209
-            traits=[],
210
-            candidates=None
211
-        )
212
-        mock_pr.return_value.provision_node.assert_called_once_with(
213
-            mock_pr.return_value.reserve_node.return_value,
214
-            image='myimg',
215
-            nics=[{'network': 'mynet'}],
216
-            root_disk_size=None,
217
-            config=mock.ANY,
218
-            hostname=None,
219
-            netboot=False,
220
-            wait=1800)
177
+        self._check(mock_pr, args, {}, {})
221 178
 
222 179
         mock_log.basicConfig.assert_called_once_with(level=mock_log.DEBUG,
223 180
                                                      format=mock.ANY)
@@ -228,29 +185,10 @@ class TestDeploy(testtools.TestCase):
228 185
             mock_log.getLogger.mock_calls)
229 186
 
230 187
     @mock.patch.object(_cmd, 'logging', autospec=True)
231
-    def test_args_quiet(self, mock_log, mock_os_conf, mock_pr):
188
+    def test_args_quiet(self, mock_log, mock_pr):
232 189
         args = ['--quiet', 'deploy', '--network', 'mynet', '--image', 'myimg',
233 190
                 '--resource-class', 'compute']
234
-        _cmd.main(args)
235
-        mock_pr.assert_called_once_with(
236
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
237
-            dry_run=False)
238
-        mock_pr.return_value.reserve_node.assert_called_once_with(
239
-            resource_class='compute',
240
-            conductor_group=None,
241
-            capabilities={},
242
-            traits=[],
243
-            candidates=None
244
-        )
245
-        mock_pr.return_value.provision_node.assert_called_once_with(
246
-            mock_pr.return_value.reserve_node.return_value,
247
-            image='myimg',
248
-            nics=[{'network': 'mynet'}],
249
-            root_disk_size=None,
250
-            config=mock.ANY,
251
-            hostname=None,
252
-            netboot=False,
253
-            wait=1800)
191
+        self._check(mock_pr, args, {}, {})
254 192
 
255 193
         mock_log.basicConfig.assert_called_once_with(level=mock_log.CRITICAL,
256 194
                                                      format=mock.ANY)
@@ -263,29 +201,10 @@ class TestDeploy(testtools.TestCase):
263 201
         self.assertFalse(self.mock_print.called)
264 202
 
265 203
     @mock.patch.object(_cmd, 'logging', autospec=True)
266
-    def test_args_verbose_1(self, mock_log, mock_os_conf, mock_pr):
204
+    def test_args_verbose_1(self, mock_log, mock_pr):
267 205
         args = ['-v', 'deploy', '--network', 'mynet', '--image', 'myimg',
268 206
                 '--resource-class', 'compute']
269
-        _cmd.main(args)
270
-        mock_pr.assert_called_once_with(
271
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
272
-            dry_run=False)
273
-        mock_pr.return_value.reserve_node.assert_called_once_with(
274
-            resource_class='compute',
275
-            conductor_group=None,
276
-            capabilities={},
277
-            traits=[],
278
-            candidates=None
279
-        )
280
-        mock_pr.return_value.provision_node.assert_called_once_with(
281
-            mock_pr.return_value.reserve_node.return_value,
282
-            image='myimg',
283
-            nics=[{'network': 'mynet'}],
284
-            root_disk_size=None,
285
-            config=mock.ANY,
286
-            hostname=None,
287
-            netboot=False,
288
-            wait=1800)
207
+        self._check(mock_pr, args, {}, {})
289 208
 
290 209
         mock_log.basicConfig.assert_called_once_with(level=mock_log.WARNING,
291 210
                                                      format=mock.ANY)
@@ -296,29 +215,10 @@ class TestDeploy(testtools.TestCase):
296 215
             mock_log.getLogger.mock_calls)
297 216
 
298 217
     @mock.patch.object(_cmd, 'logging', autospec=True)
299
-    def test_args_verbose_2(self, mock_log, mock_os_conf, mock_pr):
218
+    def test_args_verbose_2(self, mock_log, mock_pr):
300 219
         args = ['-vv', 'deploy', '--network', 'mynet', '--image', 'myimg',
301 220
                 '--resource-class', 'compute']
302
-        _cmd.main(args)
303
-        mock_pr.assert_called_once_with(
304
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
305
-            dry_run=False)
306
-        mock_pr.return_value.reserve_node.assert_called_once_with(
307
-            resource_class='compute',
308
-            conductor_group=None,
309
-            capabilities={},
310
-            traits=[],
311
-            candidates=None
312
-        )
313
-        mock_pr.return_value.provision_node.assert_called_once_with(
314
-            mock_pr.return_value.reserve_node.return_value,
315
-            image='myimg',
316
-            nics=[{'network': 'mynet'}],
317
-            root_disk_size=None,
318
-            config=mock.ANY,
319
-            hostname=None,
320
-            netboot=False,
321
-            wait=1800)
221
+        self._check(mock_pr, args, {}, {})
322 222
 
323 223
         mock_log.basicConfig.assert_called_once_with(level=mock_log.INFO,
324 224
                                                      format=mock.ANY)
@@ -329,29 +229,10 @@ class TestDeploy(testtools.TestCase):
329 229
             mock_log.getLogger.mock_calls)
330 230
 
331 231
     @mock.patch.object(_cmd, 'logging', autospec=True)
332
-    def test_args_verbose_3(self, mock_log, mock_os_conf, mock_pr):
232
+    def test_args_verbose_3(self, mock_log, mock_pr):
333 233
         args = ['-vvv', 'deploy', '--network', 'mynet', '--image', 'myimg',
334 234
                 '--resource-class', 'compute']
335
-        _cmd.main(args)
336
-        mock_pr.assert_called_once_with(
337
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
338
-            dry_run=False)
339
-        mock_pr.return_value.reserve_node.assert_called_once_with(
340
-            resource_class='compute',
341
-            conductor_group=None,
342
-            capabilities={},
343
-            traits=[],
344
-            candidates=None
345
-        )
346
-        mock_pr.return_value.provision_node.assert_called_once_with(
347
-            mock_pr.return_value.reserve_node.return_value,
348
-            image='myimg',
349
-            nics=[{'network': 'mynet'}],
350
-            root_disk_size=None,
351
-            config=mock.ANY,
352
-            hostname=None,
353
-            netboot=False,
354
-            wait=1800)
235
+        self._check(mock_pr, args, {}, {})
355 236
 
356 237
         mock_log.basicConfig.assert_called_once_with(level=mock_log.DEBUG,
357 238
                                                      format=mock.ANY)
@@ -362,7 +243,7 @@ class TestDeploy(testtools.TestCase):
362 243
             mock_log.getLogger.mock_calls)
363 244
 
364 245
     @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
365
-    def test_reservation_failure(self, mock_log, mock_os_conf, mock_pr):
246
+    def test_reservation_failure(self, mock_log, mock_pr):
366 247
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
367 248
                 '--resource-class', 'compute']
368 249
         failure = RuntimeError('boom')
@@ -371,7 +252,7 @@ class TestDeploy(testtools.TestCase):
371 252
         mock_log.assert_called_once_with('%s', failure, exc_info=False)
372 253
 
373 254
     @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
374
-    def test_deploy_failure(self, mock_log, mock_os_conf, mock_pr):
255
+    def test_deploy_failure(self, mock_log, mock_pr):
375 256
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
376 257
                 '--resource-class', 'compute']
377 258
         failure = RuntimeError('boom')
@@ -380,356 +261,116 @@ class TestDeploy(testtools.TestCase):
380 261
         mock_log.assert_called_once_with('%s', failure, exc_info=False)
381 262
 
382 263
     @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
383
-    def test_invalid_hostname(self, mock_log, mock_os_conf, mock_pr):
264
+    def test_invalid_hostname(self, mock_log, mock_pr):
384 265
         args = ['deploy', '--hostname', 'n_1', '--image', 'myimg',
385 266
                 '--resource-class', 'compute']
386 267
         self.assertRaises(SystemExit, _cmd.main, args)
387 268
         self.assertTrue(mock_log.called)
388 269
 
389
-    def test_args_capabilities(self, mock_os_conf, mock_pr):
270
+    def test_args_capabilities(self, mock_pr):
390 271
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
391 272
                 '--capability', 'foo=bar', '--capability', 'answer=42',
392 273
                 '--resource-class', 'compute']
393
-        _cmd.main(args)
394
-        mock_pr.assert_called_once_with(
395
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
396
-            dry_run=False)
397
-        mock_pr.return_value.reserve_node.assert_called_once_with(
398
-            resource_class='compute',
399
-            conductor_group=None,
400
-            capabilities={'foo': 'bar', 'answer': '42'},
401
-            traits=[],
402
-            candidates=None
403
-        )
404
-        mock_pr.return_value.provision_node.assert_called_once_with(
405
-            mock_pr.return_value.reserve_node.return_value,
406
-            image='myimg',
407
-            nics=[{'network': 'mynet'}],
408
-            root_disk_size=None,
409
-            config=mock.ANY,
410
-            hostname=None,
411
-            netboot=False,
412
-            wait=1800)
413
-
414
-    def test_args_traits(self, mock_os_conf, mock_pr):
274
+        self._check(mock_pr, args,
275
+                    {'capabilities': {'foo': 'bar', 'answer': '42'}}, {})
276
+
277
+    def test_args_traits(self, mock_pr):
415 278
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
416 279
                 '--trait', 'foo:bar', '--trait', 'answer:42',
417 280
                 '--resource-class', 'compute']
418
-        _cmd.main(args)
419
-        mock_pr.assert_called_once_with(
420
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
421
-            dry_run=False)
422
-        mock_pr.return_value.reserve_node.assert_called_once_with(
423
-            resource_class='compute',
424
-            conductor_group=None,
425
-            capabilities={},
426
-            traits=['foo:bar', 'answer:42'],
427
-            candidates=None
428
-        )
429
-        mock_pr.return_value.provision_node.assert_called_once_with(
430
-            mock_pr.return_value.reserve_node.return_value,
431
-            image='myimg',
432
-            nics=[{'network': 'mynet'}],
433
-            root_disk_size=None,
434
-            config=mock.ANY,
435
-            hostname=None,
436
-            netboot=False,
437
-            wait=1800)
438
-
439
-    def test_args_configdrive(self, mock_os_conf, mock_pr):
281
+        self._check(mock_pr, args,
282
+                    {'traits': ['foo:bar', 'answer:42']}, {})
283
+
284
+    def test_args_configdrive(self, mock_pr):
440 285
         with tempfile.NamedTemporaryFile() as fp:
441 286
             fp.write(b'foo\n')
442 287
             fp.flush()
443 288
 
444 289
             args = ['deploy', '--network', 'mynet', '--image', 'myimg',
445 290
                     '--ssh-public-key', fp.name, '--resource-class', 'compute']
446
-            _cmd.main(args)
447
-            mock_pr.assert_called_once_with(
448
-                cloud_region=mock_os_conf.return_value.get_one.return_value,
449
-                dry_run=False)
450
-            mock_pr.return_value.reserve_node.assert_called_once_with(
451
-                resource_class='compute',
452
-                conductor_group=None,
453
-                capabilities={},
454
-                traits=[],
455
-                candidates=None
456
-            )
457
-            mock_pr.return_value.provision_node.assert_called_once_with(
458
-                mock_pr.return_value.reserve_node.return_value,
459
-                image='myimg',
460
-                nics=[{'network': 'mynet'}],
461
-                root_disk_size=None,
462
-                config=mock.ANY,
463
-                hostname=None,
464
-                netboot=False,
465
-                wait=1800)
291
+            self._check(mock_pr, args, {}, {})
292
+
466 293
         config = mock_pr.return_value.provision_node.call_args[1]['config']
467 294
         self.assertEqual(['foo'], config.ssh_keys)
468 295
 
469 296
     @mock.patch.object(_config.InstanceConfig, 'add_user', autospec=True)
470
-    def test_args_user_name(self, mock_add_user, mock_os_conf, mock_pr):
297
+    def test_args_user_name(self, mock_add_user, mock_pr):
471 298
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
472 299
                 '--user-name', 'banana', '--resource-class', 'compute']
473
-        _cmd.main(args)
474
-        mock_pr.assert_called_once_with(
475
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
476
-            dry_run=False)
477
-        mock_pr.return_value.reserve_node.assert_called_once_with(
478
-            resource_class='compute',
479
-            conductor_group=None,
480
-            capabilities={},
481
-            traits=[],
482
-            candidates=None
483
-        )
484
-        mock_pr.return_value.provision_node.assert_called_once_with(
485
-            mock_pr.return_value.reserve_node.return_value,
486
-            image='myimg',
487
-            nics=[{'network': 'mynet'}],
488
-            root_disk_size=None,
489
-            config=mock.ANY,
490
-            hostname=None,
491
-            netboot=False,
492
-            wait=1800)
300
+        self._check(mock_pr, args, {}, {})
301
+
493 302
         config = mock_pr.return_value.provision_node.call_args[1]['config']
494 303
         self.assertEqual([], config.ssh_keys)
495 304
         mock_add_user.assert_called_once_with(config, 'banana', sudo=False)
496 305
 
497 306
     @mock.patch.object(_config.InstanceConfig, 'add_user', autospec=True)
498
-    def test_args_user_name_with_sudo(self, mock_add_user, mock_os_conf,
499
-                                      mock_pr):
307
+    def test_args_user_name_with_sudo(self, mock_add_user, mock_pr):
500 308
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
501 309
                 '--user-name', 'banana', '--resource-class', 'compute',
502 310
                 '--passwordless-sudo']
503
-        _cmd.main(args)
504
-        mock_pr.assert_called_once_with(
505
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
506
-            dry_run=False)
507
-        mock_pr.return_value.reserve_node.assert_called_once_with(
508
-            resource_class='compute',
509
-            conductor_group=None,
510
-            capabilities={},
511
-            traits=[],
512
-            candidates=None
513
-        )
514
-        mock_pr.return_value.provision_node.assert_called_once_with(
515
-            mock_pr.return_value.reserve_node.return_value,
516
-            image='myimg',
517
-            nics=[{'network': 'mynet'}],
518
-            root_disk_size=None,
519
-            config=mock.ANY,
520
-            hostname=None,
521
-            netboot=False,
522
-            wait=1800)
311
+        self._check(mock_pr, args, {}, {})
312
+
523 313
         config = mock_pr.return_value.provision_node.call_args[1]['config']
524 314
         self.assertEqual([], config.ssh_keys)
525 315
         mock_add_user.assert_called_once_with(config, 'banana', sudo=True)
526 316
 
527
-    def test_args_port(self, mock_os_conf, mock_pr):
317
+    def test_args_port(self, mock_pr):
528 318
         args = ['deploy', '--port', 'myport', '--image', 'myimg',
529 319
                 '--resource-class', 'compute']
530
-        _cmd.main(args)
531
-        mock_pr.assert_called_once_with(
532
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
533
-            dry_run=False)
534
-        mock_pr.return_value.reserve_node.assert_called_once_with(
535
-            resource_class='compute',
536
-            conductor_group=None,
537
-            capabilities={},
538
-            traits=[],
539
-            candidates=None
540
-        )
541
-        mock_pr.return_value.provision_node.assert_called_once_with(
542
-            mock_pr.return_value.reserve_node.return_value,
543
-            image='myimg',
544
-            nics=[{'port': 'myport'}],
545
-            root_disk_size=None,
546
-            config=mock.ANY,
547
-            hostname=None,
548
-            netboot=False,
549
-            wait=1800)
550
-
551
-    def test_args_no_nics(self, mock_os_conf, mock_pr):
320
+        self._check(mock_pr, args, {}, {'nics': [{'port': 'myport'}]})
321
+
322
+    def test_args_no_nics(self, mock_pr):
552 323
         args = ['deploy', '--image', 'myimg', '--resource-class', 'compute']
553
-        _cmd.main(args)
554
-        mock_pr.assert_called_once_with(
555
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
556
-            dry_run=False)
557
-        mock_pr.return_value.reserve_node.assert_called_once_with(
558
-            resource_class='compute',
559
-            conductor_group=None,
560
-            capabilities={},
561
-            traits=[],
562
-            candidates=None
563
-        )
564
-        mock_pr.return_value.provision_node.assert_called_once_with(
565
-            mock_pr.return_value.reserve_node.return_value,
566
-            image='myimg',
567
-            nics=None,
568
-            root_disk_size=None,
569
-            config=mock.ANY,
570
-            hostname=None,
571
-            netboot=False,
572
-            wait=1800)
573
-
574
-    def test_args_networks_and_ports(self, mock_os_conf, mock_pr):
324
+        self._check(mock_pr, args, {}, {'nics': None})
325
+
326
+    def test_args_networks_and_ports(self, mock_pr):
575 327
         args = ['deploy', '--network', 'net1', '--port', 'port1',
576 328
                 '--port', 'port2', '--network', 'net2',
577 329
                 '--image', 'myimg', '--resource-class', 'compute']
578
-        _cmd.main(args)
579
-        mock_pr.assert_called_once_with(
580
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
581
-            dry_run=False)
582
-        mock_pr.return_value.reserve_node.assert_called_once_with(
583
-            resource_class='compute',
584
-            conductor_group=None,
585
-            capabilities={},
586
-            traits=[],
587
-            candidates=None
588
-        )
589
-        mock_pr.return_value.provision_node.assert_called_once_with(
590
-            mock_pr.return_value.reserve_node.return_value,
591
-            image='myimg',
592
-            nics=[{'network': 'net1'}, {'port': 'port1'},
593
-                  {'port': 'port2'}, {'network': 'net2'}],
594
-            root_disk_size=None,
595
-            config=mock.ANY,
596
-            hostname=None,
597
-            netboot=False,
598
-            wait=1800)
599
-
600
-    def test_args_hostname(self, mock_os_conf, mock_pr):
601
-        args = ['deploy', '--hostname', 'host', '--image', 'myimg',
602
-                '--resource-class', 'compute']
603
-        _cmd.main(args)
604
-        mock_pr.assert_called_once_with(
605
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
606
-            dry_run=False)
607
-        mock_pr.return_value.reserve_node.assert_called_once_with(
608
-            resource_class='compute',
609
-            conductor_group=None,
610
-            capabilities={},
611
-            traits=[],
612
-            candidates=None
613
-        )
614
-        mock_pr.return_value.provision_node.assert_called_once_with(
615
-            mock_pr.return_value.reserve_node.return_value,
616
-            image='myimg',
617
-            nics=None,
618
-            root_disk_size=None,
619
-            config=mock.ANY,
620
-            hostname='host',
621
-            netboot=False,
622
-            wait=1800)
623
-
624
-    def test_args_with_candidates(self, mock_os_conf, mock_pr):
625
-        args = ['deploy', '--hostname', 'host', '--image', 'myimg',
626
-                '--candidate', 'node1', '--candidate', 'node2']
627
-        _cmd.main(args)
628
-        mock_pr.assert_called_once_with(
629
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
630
-            dry_run=False)
631
-        mock_pr.return_value.reserve_node.assert_called_once_with(
632
-            resource_class=None,
633
-            conductor_group=None,
634
-            capabilities={},
635
-            traits=[],
636
-            candidates=['node1', 'node2']
637
-        )
638
-        mock_pr.return_value.provision_node.assert_called_once_with(
639
-            mock_pr.return_value.reserve_node.return_value,
640
-            image='myimg',
641
-            nics=None,
642
-            root_disk_size=None,
643
-            config=mock.ANY,
644
-            hostname='host',
645
-            netboot=False,
646
-            wait=1800)
647
-
648
-    def test_args_conductor_group(self, mock_os_conf, mock_pr):
649
-        args = ['deploy', '--conductor-group', 'loc1', '--image', 'myimg',
330
+        self._check(mock_pr, args, {},
331
+                    {'nics': [{'network': 'net1'}, {'port': 'port1'},
332
+                              {'port': 'port2'}, {'network': 'net2'}]})
333
+
334
+    def test_args_hostname(self, mock_pr):
335
+        args = ['deploy', '--network', 'mynet', '--image', 'myimg',
336
+                '--hostname', 'host', '--resource-class', 'compute']
337
+        self._check(mock_pr, args, {}, {'hostname': 'host'})
338
+
339
+    def test_args_with_candidates(self, mock_pr):
340
+        args = ['deploy', '--network', 'mynet', '--image', 'myimg',
341
+                '--candidate', 'node1', '--candidate', 'node2',
650 342
                 '--resource-class', 'compute']
651
-        _cmd.main(args)
652
-        mock_pr.assert_called_once_with(
653
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
654
-            dry_run=False)
655
-        mock_pr.return_value.reserve_node.assert_called_once_with(
656
-            resource_class='compute',
657
-            conductor_group='loc1',
658
-            capabilities={},
659
-            traits=[],
660
-            candidates=None
661
-        )
662
-        mock_pr.return_value.provision_node.assert_called_once_with(
663
-            mock_pr.return_value.reserve_node.return_value,
664
-            image='myimg',
665
-            nics=None,
666
-            root_disk_size=None,
667
-            config=mock.ANY,
668
-            hostname=None,
669
-            netboot=False,
670
-            wait=1800)
671
-
672
-    def test_args_http_image_with_checksum(self, mock_os_conf, mock_pr):
343
+        self._check(mock_pr, args, {'candidates': ['node1', 'node2']}, {})
344
+
345
+    def test_args_conductor_group(self, mock_pr):
346
+        args = ['deploy', '--network', 'mynet', '--image', 'myimg',
347
+                '--conductor-group', 'loc1', '--resource-class', 'compute']
348
+        self._check(mock_pr, args, {'conductor_group': 'loc1'}, {})
349
+
350
+    def test_args_http_image_with_checksum(self, mock_pr):
673 351
         args = ['deploy', '--image', 'https://example.com/image.img',
674 352
                 '--image-checksum', '95e750180c7921ea0d545c7165db66b8',
675
-                '--resource-class', 'compute']
676
-        _cmd.main(args)
677
-        mock_pr.assert_called_once_with(
678
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
679
-            dry_run=False)
680
-        mock_pr.return_value.reserve_node.assert_called_once_with(
681
-            resource_class='compute',
682
-            conductor_group=None,
683
-            capabilities={},
684
-            traits=[],
685
-            candidates=None
686
-        )
687
-        mock_pr.return_value.provision_node.assert_called_once_with(
688
-            mock_pr.return_value.reserve_node.return_value,
689
-            image=mock.ANY,
690
-            nics=None,
691
-            root_disk_size=None,
692
-            config=mock.ANY,
693
-            hostname=None,
694
-            netboot=False,
695
-            wait=1800)
353
+                '--network', 'mynet', '--resource-class', 'compute']
354
+        self._check(mock_pr, args, {}, {'image': mock.ANY})
355
+
696 356
         source = mock_pr.return_value.provision_node.call_args[1]['image']
697 357
         self.assertIsInstance(source, sources.HttpWholeDiskImage)
698 358
         self.assertEqual('https://example.com/image.img', source.url)
699 359
         self.assertEqual('95e750180c7921ea0d545c7165db66b8', source.checksum)
700 360
 
701
-    def test_args_http_image_with_checksum_url(self, mock_os_conf, mock_pr):
361
+    def test_args_http_image_with_checksum_url(self, mock_pr):
702 362
         args = ['deploy', '--image', 'http://example.com/image.img',
703 363
                 '--image-checksum', 'http://example.com/CHECKSUMS',
704
-                '--resource-class', 'compute']
705
-        _cmd.main(args)
706
-        mock_pr.assert_called_once_with(
707
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
708
-            dry_run=False)
709
-        mock_pr.return_value.reserve_node.assert_called_once_with(
710
-            resource_class='compute',
711
-            conductor_group=None,
712
-            capabilities={},
713
-            traits=[],
714
-            candidates=None
715
-        )
716
-        mock_pr.return_value.provision_node.assert_called_once_with(
717
-            mock_pr.return_value.reserve_node.return_value,
718
-            image=mock.ANY,
719
-            nics=None,
720
-            root_disk_size=None,
721
-            config=mock.ANY,
722
-            hostname=None,
723
-            netboot=False,
724
-            wait=1800)
364
+                '--network', 'mynet', '--resource-class', 'compute']
365
+        self._check(mock_pr, args, {}, {'image': mock.ANY})
366
+
725 367
         source = mock_pr.return_value.provision_node.call_args[1]['image']
726 368
         self.assertIsInstance(source, sources.HttpWholeDiskImage)
727 369
         self.assertEqual('http://example.com/image.img', source.url)
728 370
         self.assertEqual('http://example.com/CHECKSUMS', source.checksum_url)
729 371
 
730 372
     @mock.patch.object(_cmd.LOG, 'critical', autospec=True)
731
-    def test_args_http_image_without_checksum(self, mock_log, mock_os_conf,
732
-                                              mock_pr):
373
+    def test_args_http_image_without_checksum(self, mock_log, mock_pr):
733 374
         args = ['deploy', '--image', 'http://example.com/image.img',
734 375
                 '--resource-class', 'compute']
735 376
         self.assertRaises(SystemExit, _cmd.main, args)
@@ -737,53 +378,25 @@ class TestDeploy(testtools.TestCase):
737 378
         self.assertFalse(mock_pr.return_value.reserve_node.called)
738 379
         self.assertFalse(mock_pr.return_value.provision_node.called)
739 380
 
740
-    def test_args_custom_wait(self, mock_os_conf, mock_pr):
381
+    def test_args_custom_wait(self, mock_pr):
741 382
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
742 383
                 '--wait', '3600', '--resource-class', 'compute']
743
-        _cmd.main(args)
744
-        mock_pr.assert_called_once_with(
745
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
746
-            dry_run=False)
747
-        mock_pr.return_value.reserve_node.assert_called_once_with(
748
-            resource_class='compute',
749
-            conductor_group=None,
750
-            capabilities={},
751
-            traits=[],
752
-            candidates=None
753
-        )
754
-        mock_pr.return_value.provision_node.assert_called_once_with(
755
-            mock_pr.return_value.reserve_node.return_value,
756
-            image='myimg',
757
-            nics=[{'network': 'mynet'}],
758
-            root_disk_size=None,
759
-            config=mock.ANY,
760
-            hostname=None,
761
-            netboot=False,
762
-            wait=3600)
763
-
764
-    def test_args_no_wait(self, mock_os_conf, mock_pr):
384
+        self._check(mock_pr, args, {}, {'wait': 3600})
385
+
386
+    def test_args_no_wait(self, mock_pr):
765 387
         args = ['deploy', '--network', 'mynet', '--image', 'myimg',
766 388
                 '--no-wait', '--resource-class', 'compute']
767
-        _cmd.main(args)
768
-        mock_pr.assert_called_once_with(
769
-            cloud_region=mock_os_conf.return_value.get_one.return_value,
770
-            dry_run=False)
771
-        mock_pr.return_value.reserve_node.assert_called_once_with(
772
-            resource_class='compute',
773
-            conductor_group=None,
774
-            capabilities={},
775
-            traits=[],
776
-            candidates=None
777
-        )
778
-        mock_pr.return_value.provision_node.assert_called_once_with(
779
-            mock_pr.return_value.reserve_node.return_value,
780
-            image='myimg',
781
-            nics=[{'network': 'mynet'}],
782
-            root_disk_size=None,
783
-            config=mock.ANY,
784
-            hostname=None,
785
-            netboot=False,
786
-            wait=None)
389
+        self._check(mock_pr, args, {}, {'wait': None})
390
+
391
+    def test_with_root_size(self, mock_pr):
392
+        args = ['deploy', '--network', 'mynet', '--image', 'myimg',
393
+                '--root-size', '100', '--resource-class', 'compute']
394
+        self._check(mock_pr, args, {}, {'root_size_gb': 100})
395
+
396
+    def test_with_swap_size(self, mock_pr):
397
+        args = ['deploy', '--network', 'mynet', '--image', 'myimg',
398
+                '--swap-size', '4096', '--resource-class', 'compute']
399
+        self._check(mock_pr, args, {}, {'swap_size_mb': 4096})
787 400
 
788 401
 
789 402
 @mock.patch.object(_provisioner, 'Provisioner', autospec=True)

+ 39
- 1
metalsmith/test/test_provisioner.py View File

@@ -585,7 +585,26 @@ abcd  image
585 585
         self.assertFalse(self.api.release_node.called)
586 586
         self.assertFalse(self.conn.network.delete_port.called)
587 587
 
588
-    def test_with_root_disk_size(self):
588
+    def test_with_root_size(self):
589
+        self.updates['/instance_info/root_gb'] = 50
590
+
591
+        self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
592
+                               root_size_gb=50)
593
+
594
+        self.conn.network.create_port.assert_called_once_with(
595
+            network_id=self.conn.network.find_network.return_value.id)
596
+        self.api.attach_port_to_node.assert_called_once_with(
597
+            self.node.uuid, self.conn.network.create_port.return_value.id)
598
+        self.api.update_node.assert_called_once_with(self.node, self.updates)
599
+        self.api.validate_node.assert_called_once_with(self.node,
600
+                                                       validate_deploy=True)
601
+        self.api.node_action.assert_called_once_with(self.node, 'active',
602
+                                                     configdrive=mock.ANY)
603
+        self.assertFalse(self.wait_mock.called)
604
+        self.assertFalse(self.api.release_node.called)
605
+        self.assertFalse(self.conn.network.delete_port.called)
606
+
607
+    def test_with_deprecated_root_size(self):
589 608
         self.updates['/instance_info/root_gb'] = 50
590 609
 
591 610
         self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
@@ -604,6 +623,25 @@ abcd  image
604 623
         self.assertFalse(self.api.release_node.called)
605 624
         self.assertFalse(self.conn.network.delete_port.called)
606 625
 
626
+    def test_with_swap_size(self):
627
+        self.updates['/instance_info/swap_mb'] = 4096
628
+
629
+        self.pr.provision_node(self.node, 'image', [{'network': 'network'}],
630
+                               swap_size_mb=4096)
631
+
632
+        self.conn.network.create_port.assert_called_once_with(
633
+            network_id=self.conn.network.find_network.return_value.id)
634
+        self.api.attach_port_to_node.assert_called_once_with(
635
+            self.node.uuid, self.conn.network.create_port.return_value.id)
636
+        self.api.update_node.assert_called_once_with(self.node, self.updates)
637
+        self.api.validate_node.assert_called_once_with(self.node,
638
+                                                       validate_deploy=True)
639
+        self.api.node_action.assert_called_once_with(self.node, 'active',
640
+                                                     configdrive=mock.ANY)
641
+        self.assertFalse(self.wait_mock.called)
642
+        self.assertFalse(self.api.release_node.called)
643
+        self.assertFalse(self.conn.network.delete_port.called)
644
+
607 645
     def test_with_capabilities(self):
608 646
         inst = self.pr.provision_node(self.node, 'image',
609 647
                                       [{'network': 'network'}],

+ 2
- 0
playbooks/integration/run.yaml View File

@@ -13,6 +13,8 @@
13 13
       vars:
14 14
         image: "{{ metalsmith_whole_disk_image }}"
15 15
         image_checksum: "{{ metalsmith_whole_disk_checksum | default('') }}"
16
+        # NOTE(dtantsur): cannot specify swap with whole disk images
17
+        metalsmith_swap_size:
16 18
 
17 19
     - name: Test a partition image
18 20
       include: exercise.yaml

+ 8
- 1
roles/metalsmith_deployment/README.rst View File

@@ -35,6 +35,8 @@ The following optional variables provide the defaults for Instance_ attributes:
35 35
     the default for ``root_size``.
36 36
 ``metalsmith_ssh_public_keys``
37 37
     the default for ``ssh_public_keys``.
38
+``metalsmith_swap_size``
39
+    the default for ``swap_size``.
38 40
 ``metalsmith_traits``
39 41
     the default for ``traits``.
40 42
 ``metalsmith_user_name``
@@ -87,7 +89,7 @@ Each instances has the following attributes:
87 89
 ``resource_class`` (defaults to ``metalsmith_resource_class``)
88 90
     requested node's resource class.
89 91
 ``root_size`` (defaults to ``metalsmith_root_size``)
90
-    size of the root partition, if partition images are used.
92
+    size of the root partition (in GiB), if partition images are used.
91 93
 
92 94
     .. note::
93 95
         Also required for whole-disk images due to how the Bare Metal service
@@ -95,6 +97,9 @@ Each instances has the following attributes:
95 97
 
96 98
 ``ssh_public_keys`` (defaults to ``metalsmith_ssh_public_keys``)
97 99
     list of file names with SSH public keys to put to the node.
100
+``swap_size`` (defaults to ``metalsmith_swap_size``)
101
+    size of the swap partition (in MiB), if partition images are used
102
+    (it's an error to set it for a whole disk image).
98 103
 ``traits``
99 104
     list of traits the node should have.
100 105
 ``user_name`` (defaults to ``metalsmith_user_name``)
@@ -123,6 +128,7 @@ Example
123 128
               - hostname: compute-0
124 129
                 resource_class: compute
125 130
                 root_size: 100
131
+                swap_size: 4096
126 132
                 capabilities:
127 133
                   boot_mode: uefi
128 134
                 traits:
@@ -130,6 +136,7 @@ Example
130 136
               - hostname: compute-1
131 137
                 resource_class: compute
132 138
                 root_size: 100
139
+                swap_size: 4096
133 140
                 capabilities:
134 141
                   boot_mode: uefi
135 142
                 user_name: heat-admin

+ 2
- 1
roles/metalsmith_deployment/defaults/main.yml View File

@@ -8,8 +8,9 @@ metalsmith_netboot: false
8 8
 metalsmith_nics: []
9 9
 metalsmith_resource_class:
10 10
 metalsmith_root_size:
11
-metalsmith_traits: []
12 11
 metalsmith_ssh_public_keys: []
12
+metalsmith_swap_size:
13
+metalsmith_traits: []
13 14
 metalsmith_user_name: metalsmith
14 15
 
15 16
 # Wait parameters

+ 5
- 1
roles/metalsmith_deployment/tasks/main.yml View File

@@ -15,7 +15,10 @@
15 15
       {% endfor %}
16 16
     {% endfor %}
17 17
     {% if root_size %}
18
-      --root-disk-size {{ root_size }}
18
+      --root-size {{ root_size }}
19
+    {% endif %}
20
+    {% if swap_size %}
21
+      --swap-size {{ swap_size }}
19 22
     {% endif %}
20 23
     {% for ssh_key in ssh_public_keys %}
21 24
       --ssh-public-key {{ ssh_key }}
@@ -54,6 +57,7 @@
54 57
     root_size: "{{ instance.root_size | default(metalsmith_root_size) }}"
55 58
     ssh_public_keys: "{{ instance.ssh_public_keys | default(metalsmith_ssh_public_keys) }}"
56 59
     state: "{{ instance.state | default('present') }}"
60
+    swap_size: "{{ instance.swap_size | default(metalsmith_swap_size) }}"
57 61
     traits: "{{ instance.traits | default(metalsmith_traits) }}"
58 62
     user_name: "{{ instance.user_name | default(metalsmith_user_name) }}"
59 63
   with_items: "{{ metalsmith_instances }}"

Loading…
Cancel
Save