2주차 과제 - 호세헌

필수 과제

server list는 아래와 같이 한정된 값만 출력을 해주고 있습니다.
결과값에 project name , user name을 포함시켜서 출력되게 해주세요.

초기

data = list(compute_client.servers(**search_opts))
for s in data:
		print(s)

search_opts 조건으로 서버 목록을 조회합니다.
openstack.compute.v2.server.Server 객체들이 출력되는 것을 확인할 수 있습니다.
이 출력에는 tenant_id, user_id, location.project.name 등의 정보가 포함되어 있습니다.

openstack.compute.v2.server.Server(id=182eef16-2d78-41c0-a962-c2e75cfc0699, name=cirros-instance, status=ACTIVE, tenant_id=5bbf3e275f984ec88409ee3169110457, user_id=54c9c618ceff42478c721eab531051df, metadata={}, hostId=878c6089e0f2518b4921f22d4c2aa8bbd90b3ba516dc43db2558ba97, image=, flavor={'vcpus': 1, 'ram': 256, 'disk': 1, 'ephemeral': 0, 'swap': 0, 'original_name': 'cirros256', 'extra_specs': {'hw_rng:allowed': 'True'}}, created=2025-07-16T07:12:06Z, updated=2025-07-16T07:12:35Z, addresses={'shared': [{'version': 4, 'addr': '192.168.233.187', 'OS-EXT-IPS:type': 'fixed', 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:0b:2e:c9'}, {'version': 4, 'addr': '192.168.100.59', 'OS-EXT-IPS:type': 'floating', 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:0b:2e:c9'}]}, accessIPv4=, accessIPv6=, links=[{'rel': 'self', 'href': 'http://133.186.203.38/compute/v2.1/servers/182eef16-2d78-41c0-a962-c2e75cfc0699'}, {'rel': 'bookmark', 'href': 'http://133.186.203.38/compute/servers/182eef16-2d78-41c0-a962-c2e75cfc0699'}], OS-DCF:diskConfig=AUTO, progress=0, OS-EXT-AZ:availability_zone=nova, pinned_availability_zone=nova, OS-SCH-HNT:scheduler_hints={}, config_drive=, key_name=None, OS-SRV-USG:launched_at=2025-07-16T07:12:34.000000, OS-SRV-USG:terminated_at=None, OS-EXT-SRV-ATTR:host=devstack, OS-EXT-SRV-ATTR:instance_name=instance-00000001, OS-EXT-SRV-ATTR:hypervisor_hostname=devstack.novalocal, OS-EXT-SRV-ATTR:reservation_id=r-nrmh6cpa, OS-EXT-SRV-ATTR:launch_index=0, OS-EXT-SRV-ATTR:hostname=cirros-instance, OS-EXT-SRV-ATTR:kernel_id=, OS-EXT-SRV-ATTR:ramdisk_id=, OS-EXT-SRV-ATTR:root_device_name=/dev/vda, OS-EXT-SRV-ATTR:user_data=None, OS-EXT-STS:task_state=None, OS-EXT-STS:vm_state=active, OS-EXT-STS:power_state=1, os-extended-volumes:volumes_attached=[{'id': '9ef4368c-4fd4-464c-b737-f079be71374b', 'delete_on_termination': True}], locked=False, locked_reason=None, description=None, tags=[], trusted_image_certificates=None, host_status=UP, security_groups=[{'name': 'default'}], location=Munch({'cloud': 'envvars', 'region_name': 'RegionOne', 'zone': 'nova', 'project': Munch({'id': '5bbf3e275f984ec88409ee3169110457', `'name': 'admin'`, 'domain_id': 'default', 'domain_name': None})}))
# project_name, user_name
data = list(compute_client.servers(**search_opts))
for s in data:
    s.project_name = s.location.project.name
    s.user_name = identity_client.users.get(s.user_id).name if s.user_id else 'N/A'

문제점: identity_client.users.get()는 서버 개수만큼 API 호출이 발생합니다.

개선

projects_map = {p.id: p.name for p in identity_client.projects.list()}
users_map = {u.id: u.name for u in identity_client.users.list()}

projects_map, users_map 로 미리 수집합니다.

...

# project_name, user_name
data = list(compute_client.servers(**search_opts))
for s in data:
    s.project_name = projects_map.get(getattr(s, "project_id", getattr(s, "tenant_id", None)), "")
    s.user_name = users_map.get(getattr(s, "user_id", None), "")
        
images = {}
flavors = {}
        
...
...

columns: tuple[str, ...] = (
    'id',
    'name',
    'status',
    'project_name',
    'user_name',
)
column_headers: tuple[str, ...] = (
    'ID',
    'Name',
    'Status',
    'Project Name',
    'User Name',
)

...

선택 과제

openstack server list를 입력했을 때 take_action()함수를 찾아가는 과정 분석해보기

// launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
					... 
      "args": [
        "server",
        "list"
      ],
      "env": {
					...
      }
    }
  ]
}

1. 시작점: main() 함수

# ./python-openstackclient/openstackclient/shell.py
def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    return OpenStackShell().run(argv)

if __name__ == "__main__":
    sys.exit(main())

argv = ['server', 'list'] 가 전달되어 OpenStackShell.run(argv) 을 호출합니다.

2. OpenStackShell.run()App.run(argv) 호출

# python-openstackclient/openstackclient
class OpenStackShell(app.App):
    ...
    def __init__(..):
        ...

    def configure_logging(self):
				...
				
    def run(self, argv: list[str]) -> int:
        ret_val = 1
        self.command_options = argv
        try:
            ret_val = super().run(argv) # 부모 클래스 호출
            return ret_val
        ...

3. App.run(argv) 클래스


class App:
		...
		def run(self, argv: list[str]) -> int:
        ...
        self.options, remainder = self.parser.parse_known_args(argv) # remainder = ['server', 'list']
				...
				self.initialize_app(remainder)
				...
				self.run_subcommand(remainder)

이때 servercommand group, listcommand name입니다.

4. App.run_subcommand() 내부에서 해당 command를 로드

이 함수는 cliff의 핵심 기능 중 하나로, 'server list' 명령에 해당하는 Command 클래스를 찾아서 해당 클래스의 take_action()을 실행합니다.

def run_subcommand(self, argv: list[str]) -> int:
    ...
    subcommand = self.command_manager.find_command(argv)
    # ['server', 'list'] → ('ListServer', 'server list', ['list'])

    ...
    cmd_factory, cmd_name, sub_argv = subcommand
    ..
    cmd = cmd_factory(self, self.options, **kwargs) # → ListServer 인스턴스 생성
    ...
    result = cmd.run(parsed_args)  # → 내부에서 take_action(parsed_args) 호출
    # ListServer의 take_action() 실행
3개의 좋아요