4주차 과제 - 김윤수 (2)

Set functional test environment

  devstack-admin:
    auth:
      auth_url: http://-/identity/v3
      username: "admin"
      password: "admin"
      project_domain_name: "default"
      project_name: "admin"
      user_domain_name: "default"
    region_name: "RegionOne"
    interface: "public"
    auth_type: "password"
    identity_api_version: 3

config 위치에 유의합니다. (~/.config/openstack/clouds.yaml)

확실히 설정하기 위해서 export OS* 또한 적용해주었습니다.
아래 설정을 하고 난 뒤에는 openstack 만으로도 명령어 수행이 가능합니다.

export OS_CLOUD=devstack_cinder
export OS_CLIENT_CONFIG_FILE=/c/Users/rladb/.config/openstack/clouds.yaml

Break down test_volume_snapshot.py


#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import uuid

from openstackclient.tests.functional.volume.v3 import common


class VolumeSnapshotTests(common.BaseVolumeTests):
	#[YS] common.BaseVolumeTests 공통 테스트 유틸리티를 제공한다.
    """Functional tests for volume snapshot."""

    VOLLY = uuid.uuid4().hex
    #[YS] 테스트에서 생성할 volume의 고유 이름을 저장하는 클래스 변수이다.
    #[YS] 랜덤한 32자리 16진수 문자열 생성한다.

    @classmethod
    def setUpClass(cls):
    #[YS] cls는 해당 클래스 자체 VolumeSnapshotTests를 가리킨다.
    #[YS] 클래스 속성에 접근할 때 사용된다 ex) VOLLY, VOLUME_ID
    
        super().setUpClass()
        # create a volume for all tests to create snapshot
        #[YS] 테스트 시작 시 클래스 단위로 1GB 볼륨을 생성한다.
        
        cmd_output = cls.openstack(
            'volume create ' + '--size 1 ' + cls.VOLLY,
            parse_output=True,
        )
        cls.wait_for_status('volume', cls.VOLLY, 'available')
        #[YS] volume이 available 상태가 될 때까지 대기
        cls.VOLUME_ID = cmd_output['id']
        #[YS] 생성된 volume id를 클래스 속성으로 지정한다.

    @classmethod
    def tearDownClass(cls):
    #[YS] 테스트 종료 시 볼륨 삭제
        try:
            cls.wait_for_status('volume', cls.VOLLY, 'available')
            raw_output = cls.openstack('volume delete --force ' + cls.VOLLY)
            cls.assertOutput('', raw_output)
        finally:
            super().tearDownClass()

    def test_volume_snapshot_delete(self):
        """Test create, delete multiple"""
        
        name1 = uuid.uuid4().hex
        #[YS] uuid 기반으로 스냅샷1 생성
        cmd_output = self.openstack(
            'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY,
            parse_output=True,
        )
        #[YS] 생성 후 생성 확인
        self.assertEqual(
            name1,
            cmd_output["name"],
        )

        name2 = uuid.uuid4().hex
        #[YS] uuid 기반으로 스냅샷2 생성
        cmd_output = self.openstack(
            'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY,
            parse_output=True,
        )
        self.assertEqual(
            name2,
            cmd_output["name"],
        )

        self.wait_for_status('volume snapshot', name1, 'available')
        self.wait_for_status('volume snapshot', name2, 'available')
		#[YS] available까지 대기
        
        del_output = self.openstack(
            'volume snapshot delete ' + name1 + ' ' + name2
        )
        #[YS] 2개 삭제
        
        self.assertOutput('', del_output)
        self.wait_for_delete('volume snapshot', name1)
        self.wait_for_delete('volume snapshot', name2)
        #[YS] 삭제 확인 >> volume/base.py import

    def test_volume_snapshot_list(self):
        """Test create, list filter"""
        name1 = uuid.uuid4().hex
        #[YS] 스냅샷 생성
        cmd_output = self.openstack(
            'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY,
            parse_output=True,
        )
        self.addCleanup(self.wait_for_delete, 'volume snapshot', name1)
        #[YS] 자동 삭제 예약
        self.addCleanup(self.openstack, 'volume snapshot delete ' + name1)
        #[YS] 정보 같은지 확인
        self.assertEqual(
            name1,
            cmd_output["name"],
        )
        self.assertEqual(
            self.VOLUME_ID,
            cmd_output["volume_id"],
        )
        self.assertEqual(
            1,
            cmd_output["size"],
        )
        self.wait_for_status('volume snapshot', name1, 'available')

        name2 = uuid.uuid4().hex
        cmd_output = self.openstack(
            'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY,
            parse_output=True,
        )
        self.addCleanup(self.wait_for_delete, 'volume snapshot', name2)
        self.addCleanup(self.openstack, 'volume snapshot delete ' + name2)
        self.assertEqual(
            name2,
            cmd_output["name"],
        )
        self.assertEqual(
            self.VOLUME_ID,
            cmd_output["volume_id"],
        )
        self.assertEqual(
            1,
            cmd_output["size"],
        )
        self.wait_for_status('volume snapshot', name2, 'available')
        raw_output = self.openstack(
            'volume snapshot set ' + '--state error ' + name2
        )
        self.assertOutput('', raw_output)

        # Test list --long, --status
        cmd_output = self.openstack(
            'volume snapshot list ' + '--long ' + '--status error',
            parse_output=True,
        )
        names = [x["Name"] for x in cmd_output]
        self.assertNotIn(name1, names)
        self.assertIn(name2, names)

        # Test list --volume
        cmd_output = self.openstack(
            'volume snapshot list ' + '--volume ' + self.VOLLY,
            parse_output=True,
        )
        names = [x["Name"] for x in cmd_output]
        self.assertIn(name1, names)
        self.assertIn(name2, names)

        # Test list --name
        cmd_output = self.openstack(
            'volume snapshot list ' + '--name ' + name1,
            parse_output=True,
        )
        names = [x["Name"] for x in cmd_output]
        self.assertIn(name1, names)
        self.assertNotIn(name2, names)

    def test_volume_snapshot_set(self):
        """Test create, set, unset, show, delete volume snapshot"""
        name = uuid.uuid4().hex
        new_name = name + "_"
        #[YS] 옵션 넣어서 생성
        cmd_output = self.openstack(
            'volume snapshot create '
            + '--volume '
            + self.VOLLY
            + ' --description aaaa '
            + '--property Alpha=a '
            + name,
            parse_output=True,
        )
        #[YS] 삭제 예약
        self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name)
        self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name)
        #[YS] 값 확인
        self.assertEqual(
            name,
            cmd_output["name"],
        )
        self.assertEqual(
            1,
            cmd_output["size"],
        )
        self.assertEqual(
            'aaaa',
            cmd_output["description"],
        )
        self.assertEqual(
            {'Alpha': 'a'},
            cmd_output["properties"],
        )
        self.wait_for_status('volume snapshot', name, 'available')

        # Test volume snapshot set
        raw_output = self.openstack(
            'volume snapshot set '
            + '--name '
            + new_name
            + ' --description bbbb '
            + '--property Alpha=c '
            + '--property Beta=b '
            + name,
        )
        self.assertOutput('', raw_output)

        # Show snapshot set result
        cmd_output = self.openstack(
            'volume snapshot show ' + new_name,
            parse_output=True,
        )
        self.assertEqual(
            new_name,
            cmd_output["name"],
        )
        self.assertEqual(
            1,
            cmd_output["size"],
        )
        self.assertEqual(
            'bbbb',
            cmd_output["description"],
        )
        self.assertEqual(
            {'Alpha': 'c', 'Beta': 'b'},
            cmd_output["properties"],
        )

        # Test volume snapshot unset
        raw_output = self.openstack(
            'volume snapshot unset ' + '--property Alpha ' + new_name,
        )
        self.assertOutput('', raw_output)

        cmd_output = self.openstack(
            'volume snapshot show ' + new_name,
            parse_output=True,
        )
        self.assertEqual(
            {'Beta': 'b'},
            cmd_output["properties"],
        )

        # Test volume snapshot set --no-property
        raw_output = self.openstack(
            'volume snapshot set ' + '--no-property ' + new_name,
        )
        self.assertOutput('', raw_output)
        cmd_output = self.openstack(
            'volume snapshot show ' + new_name,
            parse_output=True,
        )
        self.assertNotIn(
            {'Beta': 'b'},
            cmd_output["properties"],
        )

Target output

  1. openstack volume snapshot show $snapshot-name


    Fields 확인 필요

  2. openstack volume snapshot unset $change_value


    change value 변경 필요

Type functional test code

  1. openstack volume snapshot show $snapshot-name
    def test_volume_snapshot_show(self):
        """Test showing a volume snapshot"""

        # 1. UUID 기반 이름 생성
        name1 = uuid.uuid4().hex

        # 2. Snapshot 생성
        cmd_output = self.openstack(
            'volume snapshot create'
            + ' --volume '
            + self.VOLLY
            + ' --description "test snapshot"'
            + ' --property test_key=test_value '
            + name1,
            parse_output=True,
        )

        # 3. 삭제 예약
        self.addCleanup(self.wait_for_delete, 'volume snapshot', name1)
        self.addCleanup(self.openstack, 'volume snapshot delete ' + name1)

        # 4. 상태 대기
        self.wait_for_status('volume snapshot', name1, 'available')

        # 5.show 부르기
        snapshot_info = self.openstack(
            'volume snapshot show ' + name1,
            parse_output=True,
        )
        print(snapshot_info)

        # 6. 값 확인하기
        self.assertEqual(name1, snapshot_info['name'])
        self.assertEqual(cmd_output['id'], snapshot_info['id'])
        self.assertEqual(cmd_output['created_at'], snapshot_info['created_at'])
        self.assertEqual(self.VOLUME_ID, snapshot_info["volume_id"])
        self.assertEqual("test snapshot", snapshot_info["description"])
        self.assertEqual(
            {"test_key": "test_value"}, snapshot_info["properties"]
        )
        self.assertEqual(1, snapshot_info["size"])
        self.assertEqual("available", snapshot_info["status"])


위와 같이 microsecond 단위로 인해 문제가 발생하므로 수정해줬습니다.

self.assertEqual(cmd_output['created_at'][:16] , snapshot_info['created_at'][:16])

microsecond 단위를 제외하고 비교하도록 파싱했습니다. 실시간성이 중요한 게 아니니 괜찮겠죠? >> 이건 질문하도록 하겠습니다

  1. openstack volume snapshot unset $change_value
    def test_volume_snapshot_unset(self):
        """Test unsetting properies of a volume snapshot"""

        name = uuid.uuid4().hex

        cmd_output = self.openstack(
            'volume snapshot create'
            + ' --volume '
            + self.VOLLY
            + ' --property Alpha=a '
            + name,
            parse_output=True,
        )

        self.addCleanup(self.wait_for_delete, 'volume snapshot', name)
        self.addCleanup(self.openstack, 'volume snapshot delete ' + name)

        self.wait_for_status('volume snapshot', name, 'available')

        raw_output = self.openstack(
            'volume snapshot unset ' + '--property Alpha ' + name,
        )

        self.assertOutput('', raw_output)

        cmd_output = self.openstack(
            'volume snapshot show ' + name,
            parse_output=True,
        )
        self.assertEqual({}, cmd_output["properties"])

Test result

tox -e functional -- openstackclient.tests.functional.volume.v3.test_volume_snapshot.VolumeSnapshotTests

openstack volume snapshot show $snapshot-name의 created_at 필드에 대해 추가 설명을 덧붙이겠습니다.

  1. 문제 상황: snapshot createcreated_atsnapshot showcreated_at의 value가 microsecond 단위가 동일하지 않음
  2. 발생 이유: snapshot show는 원래 microsecond 단위를 *.000000으로 출력함
  3. 해결 방법: microsecond 단위는 제외하고 비교 진행