/Users/sunghyun/.config/openstack/clouds.yaml 파일로 인증 정보 생성
# /Users/sunghyun/.config/openstack/clouds.yaml
clouds:
devstack-admin:
auth:
auth_url: http://180.210.82.151/identity
username: admin
password: 2025_osc_openstack
project_name: admin
user_domain_name: default
project_domain_name: default
region_name: RegionOne
identity_api_version: 3
volume_api_version: 3
devstack:
auth:
auth_url: http://180.210.82.151/identity
username: demo
password: 2025_osc_openstack
project_name: demo
user_domain_name: default
project_domain_name: default
region_name: RegionOne
identity_api_version: 3
volume_api_version: 3
devstack-alt:
auth:
auth_url: http://180.210.82.151/identity
username: alt_demo
password: 2025_osc_openstack
project_name: alt_demo
user_domain_name: default
project_domain_name: default
region_name: RegionOne
identity_api_version: 3
volume_api_version: 3
devstack-system-admin:
auth:
auth_url: http://180.210.82.151/identity
username: admin
password: 2025_osc_openstack
project_name: admin
user_domain_name: default
project_domain_name: default
region_name: RegionOne
identity_api_version: 3
volume_api_version: 3
테스트 코드
# openstack/tests/functional/identity/v3/test_service.py
import uuid
from openstack.tests.functional import base # OpenStack SDK functional test 기본 클래스
from openstack import exceptions # OpenStack 예외 클래스들
class TestService(base.BaseFunctionalTest):
"""Identity v3 Service resource functional tests.
OpenStack Identity 서비스의 Service 리소스에 대한 기능 테스트
실제 DevStack 환경에서 API 호출을 통해 검증
"""
def setUp(self):
"""테스트 시작 전 초기 설정"""
super().setUp()
# operator_cloud: 관리자 권한 클라우드 연결 (devstack-admin)
self.conn = self.operator_cloud
# Admin 권한 확인 - 서비스 관리는 관리자만 가능
try:
list(self.conn.identity.services())
except Exception as e:
# 연결 실패 시 테스트 건너뛰기
self.skipTest(f"Identity service unavailable: {e}")
def test_service_create_delete(self):
"""서비스 생성 및 삭제 테스트"""
# 고유한 서비스명/타입 생성 - 테스트 간 충돌 방지
service_name = self.getUniqueString('func-service')
service_type = self.getUniqueString('func-type')
# CREATE: 새 서비스 생성
service = self.conn.identity.create_service(
name=service_name,
type=service_type,
description='Functional test service',
enabled=True # 활성화 상태로 생성
)
# 테스트 실패 시에도 리소스 정리 보장
self.addCleanup(self._cleanup_service, service.id)
# 생성 결과 검증
self.assertEqual(service.name, service_name)
self.assertEqual(service.type, service_type)
self.assertTrue(service.is_enabled) # enabled=True 확인
self.assertIsNotNone(service.id) # ID 할당 확인
# DELETE: 수동 삭제 테스트
self.conn.identity.delete_service(service.id)
# 삭제 확인 - find 메서드로 존재 여부 체크
deleted_service = self.conn.identity.find_service(
service.id, ignore_missing=True # 없어도 예외 발생 안함
)
self.assertIsNone(deleted_service) # 삭제되어 None 반환 확인
def test_service_show(self):
"""서비스 조회 기능 테스트"""
service_name = self.getUniqueString('show-service')
service_type = self.getUniqueString('show-type')
# 테스트용 서비스 생성
created_service = self.conn.identity.create_service(
name=service_name,
type=service_type
)
self.addCleanup(self._cleanup_service, created_service.id)
# READ: ID로 서비스 조회
retrieved_service = self.conn.identity.get_service(created_service.id)
# 조회 결과와 생성한 서비스 일치 확인
self.assertEqual(retrieved_service.id, created_service.id)
self.assertEqual(retrieved_service.name, service_name)
self.assertEqual(retrieved_service.type, service_type)
def test_service_list(self):
"""서비스 목록 조회 테스트"""
service_name = self.getUniqueString('list-service')
service_type = self.getUniqueString('list-type')
# 테스트용 서비스 생성
created_service = self.conn.identity.create_service(
name=service_name,
type=service_type
)
self.addCleanup(self._cleanup_service, created_service.id)
# 전체 서비스 목록 조회
services = list(self.conn.identity.services())
service_ids = [s.id for s in services] # ID 목록 추출
# 생성한 서비스가 목록에 포함되어 있는지 확인
self.assertIn(created_service.id, service_ids)
def test_service_set(self):
"""서비스 수정 기능 테스트"""
service_name = self.getUniqueString('set-service')
service_type = self.getUniqueString('set-type')
# 수정할 서비스 생성
service = self.conn.identity.create_service(
name=service_name,
type=service_type,
enabled=True # 초기값: 활성화
)
self.addCleanup(self._cleanup_service, service.id)
# UPDATE: 설명과 상태 변경
updated_description = 'Updated functional test service'
updated_service = self.conn.identity.update_service(
service.id,
description=updated_description,
enabled=False # 비활성화로 변경
)
# 수정 결과 검증
self.assertEqual(updated_service.description, updated_description)
self.assertFalse(updated_service.is_enabled) # 비활성화 확인
def test_service_find(self):
"""이름으로 서비스 찾기 테스트"""
service_name = self.getUniqueString('find-service')
service_type = self.getUniqueString('find-type')
# 찾을 서비스 생성
service = self.conn.identity.create_service(
name=service_name,
type=service_type
)
self.addCleanup(self._cleanup_service, service.id)
# 이름으로 서비스 검색
found_service = self.conn.identity.find_service(service_name)
# 검색 결과 검증
self.assertIsNotNone(found_service) # 찾기 성공
self.assertEqual(found_service.id, service.id) # 동일한 서비스 확인
def test_service_crud_workflow(self):
"""전체 CRUD 작업 순서 테스트"""
service_name = self.getUniqueString('crud-service')
service_type = self.getUniqueString('crud-type')
# CREATE: 서비스 생성
service = self.conn.identity.create_service(
name=service_name,
type=service_type,
description='CRUD workflow test'
)
service_id = service.id
# 테스트 실패 시에도 삭제 보장 - 리소스 누수 방지
self.addCleanup(self._cleanup_service, service_id)
# READ: 생성된 서비스 조회
retrieved = self.conn.identity.get_service(service_id)
self.assertEqual(retrieved.name, service_name)
# UPDATE: 서비스 정보 수정
updated = self.conn.identity.update_service(
service_id,
description='Updated CRUD test',
enabled=False
)
self.assertEqual(updated.description, 'Updated CRUD test')
self.assertFalse(updated.is_enabled)
# DELETE: 서비스 삭제
self.conn.identity.delete_service(service_id)
# 삭제 확인
deleted = self.conn.identity.find_service(
service_id, ignore_missing=True
)
self.assertIsNone(deleted)
def test_service_exception_handling(self):
"""예외 상황 처리 테스트"""
fake_service_id = 'non-existent-service-id' # 존재하지 않는 ID
# 존재하지 않는 서비스 조회 시 예외 발생 확인
try:
self.conn.identity.get_service(fake_service_id)
self.fail("Expected exception not raised") # 예외 안 발생 시 실패
except Exception:
pass # 예외 발생이 정상 동작
# 존재하지 않는 서비스 수정 시 예외 발생 확인
try:
self.conn.identity.update_service(
fake_service_id, description='This will fail'
)
self.fail("Expected exception not raised")
except Exception:
pass # 예외 발생이 정상 동작
# ignore_missing=True 옵션 테스트 - 예외 발생하면 안됨
try:
self.conn.identity.delete_service(fake_service_id, ignore_missing=True)
except Exception as e:
self.fail(f"delete_service with ignore_missing=True should not raise: {e}")
def test_service_name_uniqueness(self):
"""서비스 이름 중복 허용 여부 테스트"""
service_name = self.getUniqueString('unique-service')
service_type1 = self.getUniqueString('unique-type1')
service_type2 = self.getUniqueString('unique-type2')
# 첫 번째 서비스 생성
service1 = self.conn.identity.create_service(
name=service_name,
type=service_type1
)
self.addCleanup(self._cleanup_service, service1.id)
# 같은 이름, 다른 타입으로 두 번째 서비스 생성
# OpenStack에서는 이름 중복이 허용됨
service2 = self.conn.identity.create_service(
name=service_name,
type=service_type2
)
self.addCleanup(self._cleanup_service, service2.id)
# 두 서비스가 모두 생성되었는지 확인 (서로 다른 ID)
self.assertNotEqual(service1.id, service2.id)
def _cleanup_service(self, service_id):
"""테스트용 서비스 정리 헬퍼 메서드
Args:
service_id: 삭제할 서비스 ID
"""
try:
# ignore_missing=True: 이미 삭제된 경우 에러 무시
self.conn.identity.delete_service(service_id, ignore_missing=True)
except Exception:
# 모든 예외 무시 - cleanup은 실패해도 테스트에 영향 없음
pass
openstack service 클래스에 관한 테스트
tox -e functional -- openstack.tests.functional.identity.v3.test_service
