1. Network A -> Network A

PREROUTING(nat:dnat) -> INPUT(filter) -> OUTPUT(nat:dnat) -> OUTPUT(filter->POSTROUTING(nat:snat)


2. Network A -> Network B

PREROUTING(nat:dnat) -> FORWARD(filter) -> POSTROUTING(nat:snat)


3. Nova Instance 생성 후 iptables nat

PREROUTING ACCEPT

    nova-network-PREROUTING

        -> VM DNAT 변환

    nova-compute-PREROUTING

    nova-api-metadat-PREROUTING

INPUT ACCEPT

OUTPUT ACCEPT

    nova-network-OUTPUT

        -> VM DNAT 변환

    nova-compute-OUTPUT

    nova-api-metadat-OUTPUT

POSTROUTING ACCEPT

    nova-network-POSTROUTING

    nova-compute-POSTROUTING

    nova-api-metadat-POSTROUTING

    nova-postrouting-bottom

        nova-network-snat

            nova-network-float-snat

                -> VM SNAT 변환

            

            -> Host SNAT 변환

        nova-compute-snat

            nova-compute-float-snat

        nova-api-metadat-snat

            nova-api-metadat-float-snat


4. Nova Instance 생성 후 iptables filter

INPUT ACCEPT

    nova-compute-INPUT

    nova-network-INPUT

        - dhcp 열기 (bridge 단위)

    nova-api-metadat-INPUT

        - nova metadata api 포트 8775 승인

FORWARD ACCEPT

    nova-filter-top

        nova-compute-local

            - nova-compute-inst-732 (인스턴스별 생성)

                nova-compute-provider

                - Secutiry rules 입력

                nova-compute-sg-fallback

                    - 모든 패킷 drop

        nova-network-local

        nova-api-metadat-local

    nova-compute-FORWARD

    nova-network-FORWARD

        - bridge 별 in/out 패킷 승인

    nova-api-metadat-FORWARD

OUTPUT ACCEPT

    nova-filter-top

        nova-compute-local

            - nova-compute-inst-732 (인스턴스별 생성)

                nova-compute-provider

                - Secutiry rules 입력

                nova-compute-sg-fallback

                    - 모든 패킷 drop

        nova-network-local

        nova-api-metadat-local

    nova-compute-OUTPUT

    nova-network-OUTPUT

    nova-api-metadat-OUTPUT





Posted by Kubernetes Korea co-leader seungkyua@gmail.com

딱딱한 보고서로는 제가 보고 느낀 것을 제대로 보고할 수 없을 것 같아 이렇게 블로그라도 올립니다.


OpenStack Design Summit 뿐만 아니라 여러 회사가 모이는 컨퍼런스는 정말 중요한 것 같습니다.


사실 하나도 모르는 상태에서 관련 컨퍼런스를 참석한다면 아마도 1/3 도 못 얻어갈겁니다.

그러나 어느 정도 관련 기술을 활용하여 프로덕트를 만들고 있는 상태라면 일부 문제에 대한 해답을 찾을 수 있습니다.


1. 새로운 기술, 프로덕트의 장

    - 이번 컨퍼런스에서 주로 논의된 것은 클라우드에 맞게 기존의 네트워크의 개념을 뒤엎는 내용이었습니다.

      현재 우리가 구축한 시스템도 네트워크 측면에서 Scale out 에 문제가 있습니다.

      사실, 국내에서는 아무도 답을 줄 수 없었죠. 그리고 기존 자기 기술의 틀에 갇혀 해답이 없었습니다.

      저희 시스템도 당분간은 문제 없습니다. 고객이 빠르게 증가한다고 하더라도 어느 정도 확장이 가능합니다.

      근본적인 해결책은 아니더라도 말이죠.

      

      이곳에는 그런 문제들을 고민하고 해결하기 위한 많은 업체들이 자신이 주장하는 것을 제품으로 만들어 

      참여합니다. 완벽하지는 않지만 어느 정도는 서비스가 가능한 제품들이죠.

      그리고 그 제품을 팔기 위해 엔지니어들을 논리적으로 설득시킵니다. 물론 설득에 실패하면 제품도

      쓸데없어 지겠지요.


      하지만 중요한 것은 문제를 해결할 수 있는 기술과 개념입니다. 

      그리고 여기서 힌트를 얻어 어느 정도는 우리도 직접 개발이 가능하다는 생각이 듭니다. 

      절반의 문제는 해결할 수 있는 그림이 그려진 것이죠..


      또한 그들의 제품을 보면서 처음에는 구현이 불가능할 것 같은 요구사항을 구현 가능한 아키텍처로까지

      그릴 수 있었습니다.

      Auto Scaling 및 Hybrid Bursting 구현이 그런 경우입니다.

       

2. 향후 주요 개발 내용 및 방법에 대한 논의

     - OpenStack 의 주요 개발 내용 및 방법은 디자인 서밋에서 결정나는 군요.

        사실 어떤 내용들이 개발될지는 어느 정도 결정이 난 상태 같습니다. 하지만 개발자들이 논의해서

        어떻게 진행할지를 결정하는 것은 인상깊었습니다. 애자일처럼 설계서없이 그림으로 논의하고

        바로 실행에 옮기는 거죠. 그것도 온라인으로.. 이것이 오픈소스 커뮤니티를 가능한게 한거 같습니다.

        물론 논의할 때 좀 무시되는 발언들도 있지만, 그래도 모든 것은 열려있으니까.


        문제는 향후 적극적 참여를 위해서는 개발자간 face to face 로 논의하고 서로를 아는 것이 중요합니다.

        일단 내공을 보여주고 나면 서로에 깊은 관심을 갖게 되는 것이 개발자들이니까요..

        그런의미에서 가능하면 많은 개발자가 디자인서밋에 참여해야 합니다.

        그리고 그 방향에 맞추어 개발에 적극 참여하여야 합니다.

        수동적으로 소스만 갖다 쓸거면 아웃사이더밖에 될 수 없습니다.


        문제는 관련 개발자가 저만 왔다는 것이 문제입니다.

        아무래도 제가 전달하면 감흥이 떨어지겠죠? 시너지 효과도 덜할 거고..

        그리고 여기서 만난 개발자들은 저밖에 기억을 못하겠죠.

        관련 시장이 얼마나 큰데.. 그리고 회사에서 얼마를 투자하는데.. 개발자 몇명을 더 못데리고 오다니..

        회사 입장에서는 소탐대실이라고 밖에 할 수 없습니다.


3. 여러 업체를 통한 현재 우리 기술력에 대한 객관적 평가

      - 같은 기술을 도입하는 여러 업체를 통해서 우리의 기술력을 객관적으로 평가할 수 있었습니다.

         우리가 해결한 고민을 질문하는 업체도 있었고, 반대로 우리가 고민한 것을 솔루션으로 만들어 오는

         업체도 있었습니다.

         또한 질문을 통해서 서로가 얼마나 고민했는지를 알 수 있고요..

         제가 판단한 우리의 수준은 중상 정도??

         

마지막으로 이 곳 실리콘밸리에서는 클라우드 관련 인력과 빅데이터 관련 인력이 없습니다.

구글, 페이스북, HP, DELL, Cisco, IBM, 등등 메이저 업체가 다 데려갔죠..

링크드인에 본인을 잘 소개해 보세요.. 실리콘밸리에서 바로 콜이 올겁니다. ^^




Posted by Kubernetes Korea co-leader seungkyua@gmail.com

Nova Boot 순서

OpenStack/Nova 2011.11.04 18:00

nova 에서 instance 를 생성할 때 호출되는 Sequence 흐름을 표현한다.
모듈별 흐름으로 보면 다음과 같다.

API -> COMPUTE -> RabbitMQ -> SCHEDULER -> RabbitMQ -> COMPUTE

1. api/openstack/servers.py -> create()
     1-1. api/openstack/create_instance_helper.py -> create_instance()
            - request 로 넘어온 값을 추출
            - glance Image 서버에 접근할 수 있는 객체 생성
            - glance 로 부터 image id, kernel id, ramdisk id 를 조회
            1-1-1. compute/instance_types.py -> get_instance_type_by_flavor_id()
                      - db로 부터 instance type 을 조회
            1-1-2. compute/api.py -> create()
                      - 넘어온 파라미터에 대한 체크
                      1-1-2-1. create_db_entry_for_new_instance()
                                   1-1-2-1-1. self.db.security_group_get_by_name()
                                                   - db에서 security group name 으로 id를 조회
                                   1-1-2-1-2. self.db.instance_create()
                                                   - instance 테이블에 기본 값만 추가하여 instance id 를 생성
                                   1-1-2-1-3. self.db.instance_add_security_group()
                                                   - security_group_instance_association 테이블에 값을 추가
                                   1-1-2-1-4. self._update_image_block_device_mapping()
                                                   - glance 에서 가져온 image 메타데이터 mappings 정보를 이용
                                                   1-1-2-1-4-1. self.db.block_device_mapping_update_or_create()
                                                                      - 파라미터로 넘어온 mappings 값이 있으면
                                                                         block_device_mapping 테이블에 저장
                                   1-1-2-1-5. self._update_block_device_mapping()
                                                   - glance 에서 가져온 image 메타데이터 block_device_mappings
                                                      정보를 이용
                                                   1-1-2-1-5-1. self.db.block_device_mapping_update_or_create()
                                                                      - 파라미터로 넘어온 mappings 값이 있으면
                                                                         block_device_mapping 테이블에 저장
                                   1-1-2-1-6. self._update_block_device_mapping()
                                                   - 파라미터로 넘어온 mappings 정보를 이용
                                                   1-1-2-1-6-1. self.db.block_device_mapping_update_or_create()
                                                                      - 파라미터로 넘어온 mappings 값이 있으면
                                                                         block_device_mapping 테이블에 저장
                                   1-1-2-1-7. self.update()
                                                   1-1-2-1-7-1. self.db.instance_update()
                                                                      - instances 테이블에 vm_state 와 task_state 값을
                                                                        BUILDING, SCHEDULING 으로 수정

                      1-1-2-2. _ask_scheduler_to_create_instance()
                                   1-1-2-2-1. rpc.cast(context,
                                                               FLAGS.scheduler_topic,
                                                               {"method": "run_instance",
                                                                    "args": {"topic": FLAGS.compute_topic,
                                                                                "instance_id": instance_id,
                                                                                "request_spec": request_spec,
                                                                                "availability_zone": availability_zone,
                                                                                "admin_password": admin_password,
                                                                                "injected_files": injected_files,
                                                                                "requested_networks": requested_networks}})
                                                   - rpc.cast 로 scheduler 의 run_instance 메소드를 호출 

2. scheduler/manager.py.SchedulerManager -> __getattr__()
    - run_instance 메소드가 없으니 __getattr__ 메소드가 호출됨
    2-1. _schedule()
           2-1-1. scheduler/multi.py -> __getattr__()
                     - schedule_run_instance 메소드가 없으니 __getattr__ 메소드가 호출
                     2-1-1-1. scheduler/chance.py.ChanceScheduler -> schedule_run_instance()
                                  - schedule_run_instance() 가 없어 AttributeError exception 이 발생
           2-1-2. scheduler/multi.py -> schedule()
                     2-1-2-1.  scheduler/chance.py.ChanceScheduler -> schedule()
           2-1-3. rpc.cast(context,
                                  db.queue_get_for(context, topic, host),
                                  {"method": method,
                                    "args": kwargs})
                     - rpc.cast 로 compute 의 run_instance 메소드를 호출  

3. compute/manager.py.ComputeManager -> run_instance()
    3-1. self._run_instance()
           3-1-1. self.db.instance_get()
                     - db 에서 instance 정보를 조회함
           3-1-2. virt/libvirt/connection.py.LibvirtConnection -> list_instances()
                     - libvirt 를 이용하여 kvm 에서 기동중인 instance vm id 를 조회
                     - instance['name'] 이 kvm 에서 조회되는 vm id 임
           3-1-3. self.db.instance_update()
                     - instances 테이블에 vm_state 와 task_state 를 BUILDING, NETWORKING 으로 변경
           - FLAGS.stub_network 이 False 이면
           3-1-3. network/api.py.API -> allocate_for_instance()
                  3-1-3-1. rpc.call(context, FLAGS.network_topic,
                                    {'method': 'allocate_for_instance',
                                     'args': args})
           3-1-4. self._instance_update()
                     3-1-4-1. self.db.instance_update()
                                  - instances 테이블에 vm_state 와 task_state 를 BUILDING,
                                     BLOCK_DEVICE_MAPPING으로 변경
           3-1-5. self._setup_block_device_mapping() 
                      3-1-5-1. self.db.block_device_mapping_get_all_by_instance()
                                   - instances 테이블과 block_device_mapping 테이블을 조인하여 조회
                                   - snapshot_id 값이 있고, volume_id 값이 없으면 아래가 호출됨
                      3-1-5-2. volume/apy.py.API -> create()
                                   3-1-5-2-1. self.db.snapshot_get()
                                   3-1-5-2-2. quota.py -> allowed_volumes()
                                   3-1-5-2-3. self.db.volume_create()
                                                   - volumes 테이블에 추가, status, attach_status 는 creating,
                                                      detached 로 세팅 
                                   3-1-5-2-4. rpc.cast(context,
                                                               FLAGS.scheduler_topic,
                                                               {"method": "create_volume",
                                                                "args": {"topic": FLAGS.volume_topic,
                                                                            "volume_id": volume['id'],
                                                                            "snapshot_id": snapshot_id}})
                                                  - rpc.cast 로 Volume 을 호출
 
                       3-1-5-3. volume/manager.py.VolumeManager -> create_volume()
                                    3-1-5-3-1. self.db.volume_get()
                                                    - volumes 테이블 정보 조회
                                    3-1-5-3-2. self.db.volume_update()
                                                    - volumes 테이블 host colume 에 떠있는 host 값 저장
                                    - snapshot_id 가 없으면 아래가 호출
                                    3-1-5-3-3. volume.san.py.HpSanISCSIDriver -> create_volume()
                                    - snapshot_id 가 있으면 아래가 호출
                                    3-1-5-3-4. self.db.snapshot_get() 
                                    3-1-5-3-5. volume.san.py.HpSanISCSIDriver ->
                                                                                              create_volume_from_snapshot()
                                    - iSCSI 를 노출
                                    3-1-5-3-6. volume.san.py.HpSanISCSIDriver -> create_export()
                                    3-1-5-3-7. self.db.volume_update()
                                                    - volumes 테이블의 status, launched_at 을 available,
                                                       utils.py -> utcnow() 로 수정

                       3-1-5-4. volume/apy.py.API -> wait_creation()
                                    - greenthread.sleep(1) 을 호출하여 volumes 테이블의 status 를 체킹
                       3-1-5-5. self.db.block_device_mapping_update()
                                    - block_device_mapping 테이블의 volume_id 값을 volumes 테이블의 id 로 세팅
                       3-1-5-6. volume/apy.py.API -> check_attach()
                                    - volumes 테이블을 조회하여 status 가 available 인지 체크
                       3-1-5-7. self._attach_volume_boot()
                                   3-1-5-7-1. volume/apy.py.API -> check_attach()
                                   3-1-5-7-2. volume/manager.py.VolumeManager -> setup_compute_volume()
                                                   - volumes 테이블을 조회하여 host 값이 현재의 host 와 같고
                                                      FLAGS.use_local_volumes 값이 True 이면
                                                   3-1-5-7-2-1. volume.san.py.HpSanISCSIDriver -> local_path()
                                                   - 아니면
                                                   3-1-5-7-2-2. volume.driver.py.ISCSIDriver -> discover_volume()
                                                                      3-1-5-7-2-2-1. self._get_iscsi_properties()
                                                                                            - self._execute('iscsiadm', '-m',
                                                                                                                  'discovery',
                                                                                                                  '-t', 'sendtargets',
                                                                                                                  '-p', volume['host'],
                                                                                                                   run_as_root=True)
                                                                                             - mount_device = ("/dev/disk/by-path/ip-
                                                                                                                       %s-iscsi-%s-lun-0" %
                                                                                                           (iscsi_properties['target_portal'],
                                                                                                            iscsi_properties['target_iqn']))
                                                                                             - export 가 안되면
                                                                                               FLAGS.num_iscsi_scan_tries만큼 반복
                                                   3-1-5-7-2-3. self.db.volume_attached()
                                                                      - volumes 테이블의 status, mountpoint, attach_status 값을
                                                                         in-use, device_name, attached 로 변경 
           3-1-6. self._instance_update()
                     3-1-6-1. self.db.instance_update()
                                  - instances 테이블에 vm_state 와 task_state 를 BUILDING, SPAWNING 으로 변경
           3-1-7. virt/libvirt/connection.py.LibvirtConnection -> spawn()
                     3-1-7-1. self.to_xml()
                                  3-1-7-1-1. self._prepare_xml_info()
                                                  3-1-7-1-1-1. virt/driver.py -> block_device_info_get_mapping()
                                                  3-1-7-1-1-2. virt/libvirt/vif.py.LibvirtBridgeDriver -> plug()
                                                                     - multi_host 가 False 이고 should_create_bridge 가 True
                                                                        - should_create_vlan 이 True 이면
                                                                           3-1-7-1-1-2-1. network/linux_net.py.
                                                                                                     LinuxBridgeInterfaceDriver
                                                                                                            -> ensure_vlan_bridge()
                                                                        - should_create_vlan 이 False 이면
                                                                           3-1-7-1-1-2-2. network/linux_net.py.
                                                                                                     LinuxBridgeInterfaceDriver 
                                                                                                            -> ensure_bridge()
                                                                     3-1-7-1-1-2-3. self._get_configurations()
                                                  3-1-7-1-1-3. compute/instance_types.py -> get_instance.type()
                                                  3-1-7-1-1-4. self._volume_in_mapping()
                                                  3-1-7-1-1-5. virt/driver.py -> block_device_info_get_ephemerals()
                                                  3-1-7-1-1-6. virt/driver.py -> block_device_info_get_root()
                                                  - root_device_name 이 있다면 그대로 데이터를 이용
                                                  - root_device_name 이 없다면
                                                  3-1-7-1-1-7. db.instance_update()
                                                  - local_device 가 True 라면
                                                  3-1-7-1-1-8. db.instance_update()
                                                  3-1-7-1-1-9. virt/driver.py -> block_device_info_get_swap()
                                                  - FLAGS.vnc_enabled and FLAGS.libvirt_type not in ('lxc', 'uml') 라면
                                                     xml_info['vncserver_host'] 및 xml_info['vnc_keymap'] 에 세팅
                     3-1-7-2. virt/libvirt/firewall.py.IptablesFirewallDriver -> setup_basic_filtering() 
                                  3-1-7-2-1. virt/libvirt/firewall.py.NWFilterFirewall -> setup_basic_filtering()
                                                  3-1-7-2-1-1. self._ensure_static_filters()
                                                  3-1-7-2-1-2. self._define_filter(self._filter_container())
                                  3-1-7-2-2. self.refresh_provider_fw_rules()
                                                  3-1-7-2-2-1. self._do_refresh_provider_fw_rules()
                                                                     3-1-7-2-2-1-1. self._purge_provider_fw_rules()
                                                                     3-1-7-2-2-1-2. self._build_provider_fw_rules()
                                                  3-1-7-2-2-2. self.iptables.apply()
                                                                     3-1-7-2-2-2-1. network/linux_net.py.IptablesManager
                                                                                                                                         -> apply()
                                                                                          3-1-7-2-2-2-1-1. self._modify_rules() 
                     3-1-7-3. virt/libvirt/firewall.py.IptablesFirewallDriver -> prepare_instance_filter()
                                  3-1-7-3-1. self.add_filters_for_instance()
                                                  3-1-7-3-1-1. network/linux_net.py.IpTablesTable -> add_chain()
                                                  3-1-7-3-1-2. self._filters_for_instance()
                                                  3-1-7-3-1-3. self._add_filters()
                                                  3-1-7-3-1-4. self.instance_rules()
                                                  3-1-7-3-1-3. self._add_filters()
                                  3-1-7-3-2. network/linux_net.py.IpTablesTable -> apply()
                                                  3-1-7-3-2-1. self._modify_rules()
                     3-1-7-4. self._create_image()
                                  .......
                     3-1-7-5. self._create_new_domain()
                                  .......
                     3-1-7-6. virt/libvirt/firewall.py.IptablesFirewallDriver -> apply_instance_filter()
                                  pass
                     3-1-7-7. utils.py.LoopingCall(_wait_for_boot)
                                  3-1-7-7-1. self.get_info()
                                                  - libvirt 로 부터 state를 주기적으로 받아와서 power_state.RUNNING 일 때
                                                     멈춤
                     3-1-7-8. utils.py.LoopingCall -> timer.start()
            3-1-8. self._get_power_state()
                     ........
           3-1-9. self._instance_update()
                     3-1-9-1. self.db.instance_update()
                                  - instances 테이블에 power_state, vm_state 와 task_state 를 libvirt를 조회하여
                                    compute/power_state.py.변수, ACTIVE, None 으로 변경
           3-1-10. utils.py -> usage_from_instance()
                      - instance 상태 변수를 넘겨서 usage_info dict 변수 값으로 변경하여 리턴
           3-1-11. notifier/api.py -> notify('compute.%s' % self.host,
                                                       'compute.instance.create',
                                                       notifier.INFO, usage_info)



Posted by Kubernetes Korea co-leader seungkyua@gmail.com
Rackspace Nova Client 를 사용하여 API 서버에 호출하는 경로

1.  novaclient/shell.main() 이 entry point 임

2.  novaclient/v1_1/shell.do_boot() 를 호출
    - instance 생성시에 "nova boot ....." 처럼 호출하면 "do_" 를 붙힌 메소드가 호출된다.

3. novaclient/v1_1/client.py 를 이용하여 novaclient/v1_1/servers.ServerManager.create() 를 호출
    - novaclient/v1_1/servers.ServerManager 는 novaclient/v1_1/base.BootingManagerWithFind 를
      상속받았으므로 self._boot() 를 호출하면 novaclient/v1_1/base.BootingManagerWithFind._boot() 가 호출됨
    - novaclient/v1_1/base.BootingManagerWithFind._boot() 에서 self._create() 가 호출되면 
      상속받은 novaclient/v1_1/base.ManagerWithFind 에서 찾고 여기에 없으므로 그 위의 클래스인
      novaclient/v1_1/base.Manager._create() 를 호출

4. novaclient/client.post() -> novaclient/client.request() 를 호출하여 HTTP Request 를 던진다.
    - standard out 으로 print 하려면 환경변수에 NOVACLIENT_DEBUG 를 추가하면 된다.


[파일 호출 순서]
novaclient/shell.py -> novaclient/v1_1/shell.py -> novaclient/v1_1/client.py -> novaclient/v1_1/servers.py 
-> novaclient/v1_1/base.py -> novaclient/base.py -> novaclient/client.py



 
Posted by Kubernetes Korea co-leader seungkyua@gmail.com
일반적으로 노바의 로그는 /var/log/nova/nova-xxx.log 형태로 남는다.

/etc/nova/nova.conf 에 보면 logdir=/var/log/nova 로 되어 있어서 해당 디렉토리에 서비스 모듈명의 이름으로 로그가 남는데 이는 프로세스별로 로그를 만드는 것이다.

만약 사용자 정의의 로그를 만들고 싶으면 다음과 같이 수정한다.

# vi /usr/lib/pymodules/python2.6/nova/log.py

def setup():
...
    reset()
    _ask_setup_log()   // 추가
...
askLogger = logging.getLogger('nova.ask')
def _ask_setup_log():

    if FLAGS.logdir:

        logname = '%s.log' % (os.path.join(FLAGS.logdir, 'nova-ask'),)

        global askLogger

        askLogger = logging.getLogger('nova.ask')

        askLogger.propagate = 0

        askHandler = WatchedFileHandler(logname)

        askHandler.setFormatter(_formatter)

        askLogger.addHandler(askHandler)

 
# vi /usr/lib/pymodules/python2.6/nova/scheduler/chance.py

...
from nova import log as logging
...

    logging.askLogger.debug('********* %s, %s *************', context, topic)


이와 같이 수정하면 /var/log/nova/nova-ask.log 가 생성되며 어떤 프로세스에서 호출하던지 해당 로그에 남게 되어 있다.


novaclient 모듈에서 로그를 추가하고 싶으면 다음과 같이 하면 된다.

# vi /usr/lib/pymodules/python2.6/novaclient/log.py

import logging

from logging.handlers import WatchedFileHandler


logger = logging.getLogger('nova.client')

logger.setLevel(logging.DEBUG)

fh = WatchedFileHandler('/var/log/nova/nova-client.log')

formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s [-] %(message)s from (pid=%(process)d) %(funcName)s %(pathname)s:%(lineno)d')

fh.setFormatter(formatter)

logger.addHandler(fh)


# vi /usr/lib/pymodules/python2.6/novaclient/shell.py

...
from log import logger
...

logger.debug('******* shell.py called !! ****************')







 
Posted by Kubernetes Korea co-leader seungkyua@gmail.com