Browse Source

Merge "Raise an SDKException/Error on duplicate hostnames"

Zuul 3 months ago
parent
commit
4c7a9cb886
2 changed files with 85 additions and 6 deletions
  1. 12
    6
      metalsmith/_utils.py
  2. 73
    0
      metalsmith/test/test_utils.py

+ 12
- 6
metalsmith/_utils.py View File

@@ -16,6 +16,7 @@
16 16
 import contextlib
17 17
 import re
18 18
 
19
+from openstack import exceptions as sdk_exc
19 20
 import six
20 21
 
21 22
 from metalsmith import exceptions
@@ -100,6 +101,12 @@ def parse_checksums(checksums):
100 101
     return result
101 102
 
102 103
 
104
+# NOTE(dtantsur): make this private since it will no longer be possible with
105
+# transition to allocation API.
106
+class DuplicateHostname(sdk_exc.SDKException, exceptions.Error):
107
+    pass
108
+
109
+
103 110
 class GetNodeMixin(object):
104 111
     """A helper mixin for getting nodes with hostnames."""
105 112
 
@@ -123,11 +130,10 @@ class GetNodeMixin(object):
123 130
         existing = [n for n in nodes
124 131
                     if n.instance_info.get(self.HOSTNAME_FIELD) == hostname]
125 132
         if len(existing) > 1:
126
-            raise RuntimeError("More than one node found with hostname "
127
-                               "%(host)s: %(nodes)s" %
128
-                               {'host': hostname,
129
-                                'nodes': ', '.join(log_res(n)
130
-                                                   for n in existing)})
133
+            raise DuplicateHostname(
134
+                "More than one node found with hostname %(host)s: %(nodes)s" %
135
+                {'host': hostname,
136
+                 'nodes': ', '.join(log_res(n) for n in existing)})
131 137
         elif not existing:
132 138
             return None
133 139
         else:
@@ -150,7 +156,7 @@ class GetNodeMixin(object):
150 156
             node = node
151 157
 
152 158
         if refresh:
153
-            return self.connection.baremetal.get_node(node)
159
+            return self.connection.baremetal.get_node(node.id)
154 160
         else:
155 161
             return node
156 162
 

+ 73
- 0
metalsmith/test/test_utils.py View File

@@ -13,9 +13,11 @@
13 13
 # See the License for the specific language governing permissions and
14 14
 # limitations under the License.
15 15
 
16
+import mock
16 17
 import testtools
17 18
 
18 19
 from metalsmith import _utils
20
+from metalsmith import exceptions
19 21
 
20 22
 
21 23
 class TestIsHostnameSafe(testtools.TestCase):
@@ -66,3 +68,74 @@ class TestIsHostnameSafe(testtools.TestCase):
66 68
         # Need to ensure a binary response for success or fail
67 69
         self.assertIsNotNone(_utils.is_hostname_safe('spam'))
68 70
         self.assertIsNotNone(_utils.is_hostname_safe('-spam'))
71
+
72
+
73
+class TestGetNodeMixin(testtools.TestCase):
74
+    def setUp(self):
75
+        super(TestGetNodeMixin, self).setUp()
76
+        self.mixin = _utils.GetNodeMixin()
77
+        self.mixin.connection = mock.Mock(spec=['baremetal'])
78
+        self.api = self.mixin.connection.baremetal
79
+
80
+    def test__get_node_with_node(self):
81
+        node = mock.Mock(spec=['id', 'name'])
82
+        result = self.mixin._get_node(node)
83
+        self.assertIs(result, node)
84
+        self.assertFalse(self.api.get_node.called)
85
+
86
+    def test__get_node_with_node_refresh(self):
87
+        node = mock.Mock(spec=['id', 'name'])
88
+        result = self.mixin._get_node(node, refresh=True)
89
+        self.assertIs(result, self.api.get_node.return_value)
90
+        self.api.get_node.assert_called_once_with(node.id)
91
+
92
+    def test__get_node_with_instance(self):
93
+        node = mock.Mock(spec=['uuid', 'node'])
94
+        result = self.mixin._get_node(node)
95
+        self.assertIs(result, node.node)
96
+        self.assertFalse(self.api.get_node.called)
97
+
98
+    def test__get_node_with_instance_refresh(self):
99
+        node = mock.Mock(spec=['uuid', 'node'])
100
+        result = self.mixin._get_node(node, refresh=True)
101
+        self.assertIs(result, self.api.get_node.return_value)
102
+        self.api.get_node.assert_called_once_with(node.node.id)
103
+
104
+    def test__get_node_with_string(self):
105
+        result = self.mixin._get_node('node')
106
+        self.assertIs(result, self.api.get_node.return_value)
107
+        self.api.get_node.assert_called_once_with('node')
108
+
109
+    def test__get_node_with_string_hostname_allowed(self):
110
+        nodes = [
111
+            mock.Mock(instance_info={'metalsmith_hostname': host})
112
+            for host in ['host1', 'host2', 'host3']
113
+        ]
114
+        self.api.nodes.return_value = nodes
115
+
116
+        result = self.mixin._get_node('host2', accept_hostname=True)
117
+        self.assertIs(result, self.api.get_node.return_value)
118
+        self.api.get_node.assert_called_once_with(nodes[1].id)
119
+
120
+    def test__get_node_with_string_hostname_allowed_fallback(self):
121
+        nodes = [
122
+            mock.Mock(instance_info={'metalsmith_hostname': host})
123
+            for host in ['host1', 'host2', 'host3']
124
+        ]
125
+        self.api.nodes.return_value = nodes
126
+
127
+        result = self.mixin._get_node('node', accept_hostname=True)
128
+        self.assertIs(result, self.api.get_node.return_value)
129
+        self.api.get_node.assert_called_once_with('node')
130
+
131
+    def test__get_node_with_string_hostname_not_unique(self):
132
+        nodes = [
133
+            mock.Mock(instance_info={'metalsmith_hostname': host})
134
+            for host in ['host1', 'host2', 'host2']
135
+        ]
136
+        self.api.nodes.return_value = nodes
137
+
138
+        self.assertRaises(exceptions.Error,
139
+                          self.mixin._get_node,
140
+                          'host2', accept_hostname=True)
141
+        self.assertFalse(self.api.get_node.called)

Loading…
Cancel
Save