From f8a42d366c0d20918fee579406feffe83c1f7b9a Mon Sep 17 00:00:00 2001
From: Ruby Loo <ruby.loo@intel.com>
Date: Wed, 20 Sep 2017 18:19:17 -0400
Subject: [PATCH] ListType preserves the order of the input

The ListType class returns a list of items in a string.
The list was not guaranteed to be the same order as in the string;
this patch changes it so that the order is preserved.

Related-Bug: #1715541
Change-Id: Iefab751d34e97f460b6d63316eb38085e4eb1154
---
 ironic/api/controllers/v1/types.py               | 13 ++++++++-----
 .../tests/unit/api/controllers/v1/test_types.py  | 16 ++++++++--------
 2 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/ironic/api/controllers/v1/types.py b/ironic/api/controllers/v1/types.py
index 7dfe12629e..a0f1cad94f 100644
--- a/ironic/api/controllers/v1/types.py
+++ b/ironic/api/controllers/v1/types.py
@@ -162,12 +162,15 @@ class ListType(wtypes.UserType):
         """Validate and convert the input to a ListType.
 
         :param value: A comma separated string of values
-        :returns: A list of unique values, whose order is not guaranteed.
+        :returns: A list of unique values (lower-cased), maintaining the
+                  same order
         """
-        items = [v.strip().lower() for v in six.text_type(value).split(',')]
-        # filter() to remove empty items
-        # set() to remove duplicated items
-        return list(set(filter(None, items)))
+        items = []
+        for v in six.text_type(value).split(','):
+            v_norm = v.strip().lower()
+            if v_norm and v_norm not in items:
+                items.append(v_norm)
+        return items
 
     @staticmethod
     def frombasetype(value):
diff --git a/ironic/tests/unit/api/controllers/v1/test_types.py b/ironic/tests/unit/api/controllers/v1/test_types.py
index eadb67ecb6..21ceac46c2 100644
--- a/ironic/tests/unit/api/controllers/v1/test_types.py
+++ b/ironic/tests/unit/api/controllers/v1/test_types.py
@@ -277,14 +277,14 @@ class TestListType(base.TestCase):
 
     def test_list_type(self):
         v = types.ListType()
-        self.assertItemsEqual(['foo', 'bar'], v.validate('foo,bar'))
-        self.assertItemsEqual(['cat', 'meow'], v.validate("cat  ,  meow"))
-        self.assertItemsEqual(['spongebob', 'squarepants'],
-                              v.validate("SpongeBob,SquarePants"))
-        self.assertItemsEqual(['foo', 'bar'],
-                              v.validate("foo, ,,bar"))
-        self.assertItemsEqual(['foo', 'bar'],
-                              v.validate("foo,foo,foo,bar"))
+        self.assertEqual(['foo', 'bar'], v.validate('foo,bar'))
+        self.assertNotEqual(['bar', 'foo'], v.validate('foo,bar'))
+
+        self.assertEqual(['cat', 'meow'], v.validate("cat  ,  meow"))
+        self.assertEqual(['spongebob', 'squarepants'],
+                         v.validate("SpongeBob,SquarePants"))
+        self.assertEqual(['foo', 'bar'], v.validate("foo, ,,bar"))
+        self.assertEqual(['foo', 'bar'], v.validate("foo,foo,foo,bar"))
         self.assertIsInstance(v.validate('foo,bar'), list)