3주차 과제 - 김유경

제출 내용 1

  1. TestServerList 클래스에 컬럼 추가
class _TestServerList(TestServer):
    columns = (
        'ID', 'Name', 'Status', 'Networks', 'Image', 'Flavor',
        'User Name',
        'Project Name',
    )
  1. Keystone Identity 모킹
def setUp(self):
    super().setUp()
    
    def get_user_side_effect(user_id):
        return User(id=user_id, name='test-user')
    
    def get_project_side_effect(project_id):
        return Project(id=project_id, name='test-project')
    
    self.identity_client.users.get.side_effect = get_user_side_effect
    self.identity_client.projects.get.side_effect = get_project_side_effect
  1. 테스트 데이터 수정
self.data = tuple(
    (
        s.id,
        s.name,
        s.status,
        server.AddressesColumn(s.addresses),
        self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
        self.flavor.name,
        'test-user',
        'test-project',
    )
    for s in self.servers
)

제출 내용 2

.diff 파일

diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index ac29650f..0d16f922 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -2857,6 +2857,16 @@ class ListServer(command.Lister):
             columns += ('project_id',)
             column_headers += ('Project ID',)
 
+        # Add user_name and project_name columns
+        columns += (
+            'user_name',
+            'project_name',
+        )
+        column_headers += (
+            'User Name',
+            'Project Name',
+        )
+
         # support for additional columns
         if parsed_args.columns:
             for c in parsed_args.columns:
@@ -2866,6 +2876,12 @@ class ListServer(command.Lister):
                 if c in ('User ID', 'user_id'):
                     columns += ('user_id',)
                     column_headers += ('User ID',)
+                if c in ('User Name', 'user_name'):
+                    columns += ('user_name',)
+                    column_headers += ('User Name',)
+                if c in ('Project Name', 'project_name'):
+                    columns += ('project_name',)
+                    column_headers += ('Project Name',)
                 if c in ('Created At', 'created_at'):
                     columns += ('created_at',)
                     column_headers += ('Created At',)
@@ -3038,6 +3054,31 @@ class ListServer(command.Lister):
             else:
                 s.security_groups_name = []
 
+        # Add user_name and project_name attributes for each server
+        for s in data:
+            # Add user_name
+            if hasattr(s, 'user_id') and s.user_id:
+                try:
+                    user_obj = identity_client.users.get(s.user_id)
+                    s.user_name = user_obj.name
+                except Exception:
+                    s.user_name = 'N/A'
+            else:
+                s.user_name = "N/A"
+
+            # Add project_name
+            project_id = getattr(s, 'project_id', None) or getattr(
+                s, 'tenant_id', None
+            )
+            if project_id:
+                try:
+                    project_obj = identity_client.projects.get(project_id)
+                    s.project_name = project_obj.name
+                except Exception:
+                    s.project_name = 'N/A'
+            else:
+                s.project_name = "N/A"
+
         # The host_status field contains the status of the compute host the
         # server is on. It is only returned by the API when the nova-api
         # policy allows. Users can look at the host_status field when, for
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 37f93b77..9b275202 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -248,6 +248,8 @@ def create_one_server(attrs=None):
             'id': 'flavor-id-' + uuid.uuid4().hex,
         },
         'OS-EXT-STS:power_state': 1,
+        'user_id': 'user-id-' + uuid.uuid4().hex,
+        'project_id': 'project-id-' + uuid.uuid4().hex,
     }
 
     # Overwrite default attributes.
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 0898c67b..60866f88 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -1295,6 +1295,8 @@ class TestServerCreate(TestServer):
             'networks': {},
             'image': self.image,
             'flavor': self.flavor,
+            'user_id': None,
+            'project_id': None,
         }
         self.server = compute_fakes.create_one_server(attrs=attrs)
 
@@ -4576,6 +4578,8 @@ class _TestServerList(TestServer):
         'Networks',
         'Image',
         'Flavor',
+        'User Name',
+        'Project Name',
     )
     columns_long = (
         'ID',
@@ -4593,6 +4597,8 @@ class _TestServerList(TestServer):
         'Host',
         'Properties',
         'Scheduler Hints',
+        'User Name',
+        'Project Name',
     )
     columns_all_projects = (
         'ID',
@@ -4602,6 +4608,8 @@ class _TestServerList(TestServer):
         'Image',
         'Flavor',
         'Project ID',
+        'User Name',
+        'Project Name',
     )
 
     def setUp(self):
@@ -4651,6 +4659,28 @@ class _TestServerList(TestServer):
         self.servers = self.setup_sdk_servers_mock(3)
         self.compute_client.servers.return_value = self.servers
 
+        # Keystone Identity에서 사용자 및 프로젝트 조회를 위한 모킹 설정
+        # 실제 OpenStack에서는 Identity API를 호출해
+        # 사용자 이름과 프로젝트 이름을 가져오지만, 테스트에서는 모킹함
+        User = collections.namedtuple('User', 'id name')
+        Project = collections.namedtuple('Project', 'id name')
+
+        # 서로 다른 user_id와 project_id에 대해 각각 다른 객체를 반환하도록 side_effect를 설정
+        def get_user_side_effect(user_id):
+            # 어떤 user_id가 들어와도 해당 ID를 가진 User 객체를 생성하지만
+            # 이름은 항상 'test-user'로 고정
+            return User(id=user_id, name='test-user')
+
+        def get_project_side_effect(project_id):
+            # 어떤 project_id가 들어와도 해당 ID를 가진 Project 객체를 생성하지만
+            # 이름은 항상 'test-project'로 고정
+            return Project(id=project_id, name='test-project')
+
+        # Identity 클라이언트의 users.get()과 projects.get() 메서드를
+        # 위에서 정의한 side_effect 함수로 모킹 설정
+        self.identity_client.users.get.side_effect = get_user_side_effect
+        self.identity_client.projects.get.side_effect = get_project_side_effect
+
         # Get the command object to test
         self.cmd = server.ListServer(self.app, None)
 
@@ -4682,6 +4712,8 @@ class TestServerList(_TestServerList):
                 # Image will be an empty string if boot-from-volume
                 self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
                 self.flavor.name,
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -4742,6 +4774,8 @@ class TestServerList(_TestServerList):
                 server.HostColumn(getattr(s, 'hypervisor_hostname')),
                 format_columns.DictColumn(s.metadata),
                 format_columns.DictListColumn(None),
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -4775,6 +4809,8 @@ class TestServerList(_TestServerList):
                 self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
                 self.flavor.name,
                 s.project_id,
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -4857,6 +4893,8 @@ class TestServerList(_TestServerList):
                 # Image will be an empty string if boot-from-volume
                 s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
                 s.flavor['id'],
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -4888,6 +4926,8 @@ class TestServerList(_TestServerList):
                 # Image will be an empty string if boot-from-volume
                 s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
                 s.flavor['id'],
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -5259,6 +5299,8 @@ class TestServerList(_TestServerList):
                 server.HostColumn(getattr(s, 'hypervisor_hostname')),
                 format_columns.DictColumn(s.metadata),
                 format_columns.DictListColumn(s.scheduler_hints),
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -5315,6 +5357,8 @@ class TestServerList(_TestServerList):
                 server.HostColumn(getattr(s, 'hypervisor_hostname')),
                 format_columns.DictColumn(s.metadata),
                 format_columns.DictListColumn(s.scheduler_hints),
+                'test-user',
+                'test-project',
                 s.host_status,
             )
             for s in servers
@@ -5337,6 +5381,8 @@ class TestServerListV273(_TestServerList):
         'Networks',
         'Image',
         'Flavor',
+        'User Name',
+        'Project Name',
     )
     columns_long = (
         'ID',
@@ -5353,6 +5399,8 @@ class TestServerListV273(_TestServerList):
         'Host',
         'Properties',
         'Scheduler Hints',
+        'User Name',
+        'Project Name',
     )
 
     def setUp(self):
@@ -5395,6 +5443,8 @@ class TestServerListV273(_TestServerList):
                 # Image will be an empty string if boot-from-volume
                 self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
                 self.flavor.name,
+                'test-user',
+                'test-project',
             )
             for s in self.servers
         )
@@ -5543,6 +5593,8 @@ class TestServerListV273(_TestServerList):
             server.AddressesColumn(None),
             '',
             '',
+            'N/A',
+            'test-project',
         )
         self.assertEqual(expected_row, partial_server)
 
@@ -8496,6 +8548,7 @@ class TestServerShow(TestServer):
             'flavor': {'id': self.flavor.id},
             'tenant_id': 'tenant-id-xxx',
             'addresses': {'public': ['10.20.30.40', '2001:db8::f']},
+            'user_id': None,
         }
         self.compute_client.get_server_diagnostics.return_value = {
             'test': 'test'
3개의 좋아요