#!/usr/bin/env python3

# based on code from git://github.com/openstack/nova.git
# nova/volume/nexenta/jsonrpc.py
#
# Copyright 2011 Nexenta Systems, Inc.
# All Rights Reserved.
#
#    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

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2012, Andy Grover <agrover@redhat.com>
#
# Test client to exercise targetd.
#

import json
import time
import socket
import base64
import ssl
from test import testlib

try:
    from urllib.request import (Request, urlopen)
except ImportError:
    from urllib2 import (Request, urlopen)

user = "admin"
password = "targetd"
host = 'localhost'
port = 18700
path = '/targetrpc'
id_num = 1
use_ssl = True
pools = ['vg-targetd/thin_pool','zfs_targetd/block_pool']
fs_pools = ['/mnt/btrfs','/zfs_targetd/fs_pool']


ctx = ssl.create_default_context(cafile=testlib.cert_file)

def jsonrequest(method, params=None):
    print("%s %s" % ("+" * 20, method))
    global id_num
    global use_ssl
    data = json.dumps(
        dict(id=id_num, method=method, params=params, jsonrpc="2.0"))
    id_num += 1
    username_pass = '%s:%s' % (user, password)
    auth = base64.b64encode(username_pass.encode('utf-8')).decode('utf-8')
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Basic %s' % (auth, )
    }
    #print('Sending JSON data: %s' % data)
    if use_ssl:
        scheme = 'https'
    else:
        scheme = 'http'
    url = "%s://%s:%s%s" % (scheme, host, port, path)
    try:
        request = Request(url, data.encode('utf-8'), headers)
        if use_ssl:
            response_obj = urlopen(request, context=ctx)
        else:
            response_obj = urlopen(request)
    except socket.error as e:
        print("error, retrying with SSL")
        url = "https://%s:%s%s" % (host, port, path)
        request = Request(url, data, headers)
        response_obj = urlopen(request, context=ctx)
        use_ssl = True
    response_data = response_obj.read().decode('utf-8')
    #print('Got response: %s' % response_data)
    response = json.loads(response_data)
    #Ensure we have version string
    assert response.get('jsonrpc') == "2.0"
    if response.get('error') is not None:
        if response['error']['code'] <= 0:
            raise Exception(response['error'].get('message', ''))
        else:
            print("Invalid error code, should be negative!")
    else:
        return response.get('result')


results = jsonrequest("export_list")
for result in results:
    print("export %s %s %s %s" % (str(result['initiator_wwn']),
                                  str(result['pool']), str(result['vol_name']),
                                  str(result['lun'])))

#sys.exit(1)

results = jsonrequest("pool_list")
for result in results:
    print("pool %s %s %s" % (str(result['name']), str(result['size']),
                             str(result['free_size'])))

#sys.exit(1)
failed = False

for pool in pools:
    print("Testing block pool: {0}".format(pool))

    results = jsonrequest("vol_list", dict(pool=pool))
    for result in results:
        print("vol %s %s %s" % (str(result['name']), str(result['size']),
                                str(result['uuid'])))

    try:
        jsonrequest('vol_create', dict(pool=pool, name="test2", size=4*1024*1024))

        try:
            jsonrequest("vol_copy",
                        dict(pool=pool, vol_orig="test2", vol_new="test2-copy"))

            try:
                jsonrequest("export_create",
                            dict(
                                pool=pool,
                                vol="test2",
                                lun=5,
                                initiator_wwn="iqn.2006-03.com.wtf.ohyeah:666"))

                print("waiting")
                time.sleep(5)
                results = jsonrequest("export_list")
                for result in results:
                    print("export %s %s %s %s %s" % (str(result['initiator_wwn']),
                                                     str(result['pool']),
                                                     str(result['vol_name']),
                                                     str(result['lun']),
                                                     str(result['vol_uuid'])))
                time.sleep(5)
                print("go!")

            finally:
                jsonrequest("export_destroy",
                            dict(
                                pool=pool,
                                vol="test2",
                                initiator_wwn="iqn.2006-03.com.wtf.ohyeah:666"))

        finally:
            jsonrequest("vol_destroy", dict(pool=pool, name="test2-copy"))

    finally:
        jsonrequest("vol_destroy", dict(pool=pool, name="test2"))
        print("done")

    print("block volumes left over:")
    results = jsonrequest("vol_list", dict(pool=pool))
    for fs in results:
        print("{pool}: {uuid} -> {name}".format(**fs))
        # No filesystems should be left
        failed = True

    if failed:
        print("Test failed")
        exit(1)

# ZFS fs tests
for fs_pool in fs_pools:
    fs_uuid = None

    print("Testing FS Pool {0}".format(fs_pool))
    try:
        jsonrequest('fs_create', dict(pool_name=fs_pool, name="test-fs", size_bytes=0))

        results = jsonrequest('fs_list')
        for fs in results:
            print("{pool}: {uuid} -> {name}".format(**fs))
            if fs['pool'] != fs_pool:
                failed = True
                raise Exception("unrecognized pool filesystem")
            fs_uuid = fs['uuid']

            ss_uuid = None

            try:
                jsonrequest('fs_snapshot', dict(fs_uuid=fs_uuid, dest_ss_name="test-snapshot"))

                ss_results = jsonrequest('ss_list', dict(fs_uuid=fs_uuid))

                has_snapshot = False
                for ss in ss_results:
                    print("{uuid} -> {name} ({timestamp})".format(**ss))
                    if ss['name'] != "test-snapshot":
                        failed = True
                        raise Exception("unrecognized snapshot")

                    ss_uuid = ss['uuid']
                    has_snapshot = True

                    clone_uuid = None

                    try:
                        jsonrequest("fs_clone", dict(fs_uuid=fs_uuid, dest_fs_name="test-clone", snapshot_id=ss_uuid))

                        clone_results = jsonrequest('fs_list')

                        has_clone = False
                        for result in clone_results:
                            print("{pool}: {uuid} -> {name}".format(**result))
                            if result['name'] == 'test-clone':
                                clone_uuid = result['uuid']
                                has_clone = True
                        if not has_clone:
                            failed = True
                            raise Exception("test-clone not found")
                    finally:
                        if clone_uuid is not None:
                            jsonrequest("fs_destroy",dict(uuid=clone_uuid))

                if not has_snapshot:
                    failed = True
                    raise Exception("test-snapshot not found")

            finally:
                if ss_uuid is not None:
                    jsonrequest("fs_snapshot_delete", dict(fs_uuid=fs_uuid, ss_uuid=ss_uuid))
    finally:
        if fs_uuid is not None:
            jsonrequest("fs_destroy", dict(uuid=fs_uuid))

    print("fs'es left over:")
    results = jsonrequest("fs_list")
    for fs in results:
        print("{pool}: {uuid} -> {name}".format(**fs))
        # No filesystems should be left
        failed = True

    if failed:
        print("Test failed")
        exit(1)