{"action":"created","comment":{"url":"https://api.github.com/repos/avocado-framework/avocado/pulls/comments/66243640","id":66243640,"diff_hunk":"@@ -0,0 +1,517 @@\n+# This program is free software; you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation; either version 2 of the License, or\n+# (at your option) any later version.\n+#\n+# This program is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n+#\n+# See LICENSE for more details.\n+#\n+# Author: Harish <harisrir@linux.vnet.ibm.com>\n+# Copyright: 2016 IBM\n+#\n+# Based on the code by\n+# Author: Plamen Dimitrov\n+# Copyright: Intra2net AG 2012\n+\n+\"\"\"\n+Utility for taking shapshots from existing Logical volumes\n+or creates such.\n+\n+:param vg_name: Name of the volume group.\n+:param lv_name: Name of the logical volume.\n+:param lv_size: Size of the logical volume as string in the form \"#G\"\n+        (for example 30G).\n+:param lv_snapshot_name: Name of the snapshot with origin the logical\n+        volume.\n+:param lv_snapshot_size: Size of the snapshot with origin the logical\n+        volume also as \"#G\".\n+:param ramdisk_vg_size: Size of the ramdisk virtual group.\n+:param ramdisk_basedir: Base directory for the ramdisk sparse file.\n+:param ramdisk_sparse_filename: Name of the ramdisk sparse file.\n+:param disk: Name of the disk to create volume group. If not provided,\n+             creates loop disk and creates volume group on it.\n+\n+Sample ramdisk params:\n+- ramdisk_vg_size = \"40000\"\n+- ramdisk_basedir = \"/tmp\"\n+- ramdisk_sparse_filename = \"virtual_hdd\"\n+\n+Sample general params:\n+- vg_name='autotest_vg',\n+- lv_name='autotest_lv',\n+- lv_size='1G',\n+- lv_snapshot_name='autotest_sn',\n+- lv_snapshot_size='1G'\n+The ramdisk volume group size is in MB.\n+\"\"\"\n+\n+import logging\n+import os\n+import re\n+import shutil\n+import time\n+from . import process\n+\n+LOGGER = logging.getLogger('avocado.test')\n+\n+\n+def get_diskspace(disk):\n+    \"\"\"\n+    Get the entire disk space of a given disk\n+    :return: size in bytes\n+    \"\"\"\n+\n+    result = process.system_output('fdisk -l %s' % disk, shell=True)\n+    results = result.splitlines()\n+    for line in results:\n+        if line.startswith('Disk ' + disk):\n+            pattern = re.compile(r\", (.*?) bytes\")\n+            space = pattern.findall(line)[0]\n+    return space\n+\n+\n+def vg_ramdisk(disk, vg_name, ramdisk_vg_size,\n+               ramdisk_basedir, ramdisk_sparse_filename):\n+    \"\"\"\n+    Create vg on top of ram memory to speed up lv performance.\n+    \"\"\"\n+    vg_size = ramdisk_vg_size\n+    vg_ramdisk_dir = os.path.join(ramdisk_basedir, vg_name)\n+    ramdisk_filename = os.path.join(vg_ramdisk_dir,\n+                                    ramdisk_sparse_filename)\n+    vg_ramdisk_cleanup(ramdisk_filename, vg_ramdisk_dir, vg_name)\n+    result = \"\"\n+    if not os.path.exists(vg_ramdisk_dir):\n+        os.mkdir(vg_ramdisk_dir)\n+    try:\n+        LOGGER.debug(\"Mounting tmpfs\")\n+        result = process.run(\"mount -t tmpfs tmpfs %s\" %\n+                             vg_ramdisk_dir, shell=True)\n+\n+        LOGGER.debug(\"Converting and copying /dev/zero\")\n+        if disk:\n+            vg_size = get_diskspace(disk)\n+        cmd = (\"dd if=/dev/zero of=%s bs=1M count=1 seek=%s\" %\n+               (ramdisk_filename, vg_size))\n+        result = process.run(cmd, shell=True)\n+        if not disk:\n+            LOGGER.debug(\"Finding free loop device\")\n+            result = process.run(\"losetup --find\", shell=True)\n+    except process.CmdError, ex:\n+        LOGGER.error(ex)\n+        vg_ramdisk_cleanup(ramdisk_filename, vg_ramdisk_dir, vg_name)\n+        raise ex\n+\n+    if not disk:\n+        loop_device = result.stdout.rstrip()\n+    else:\n+        loop_device = disk\n+    try:\n+        if not disk:\n+            LOGGER.debug(\"Creating loop device\")\n+            result = process.run(\"losetup %s %s\" %\n+                                 (loop_device, ramdisk_filename), shell=True)\n+        LOGGER.debug(\"Creating physical volume %s\", loop_device)\n+        result = process.run(\"pvcreate %s\" % loop_device, shell=True)\n+        LOGGER.debug(\"Creating volume group %s\", vg_name)\n+        result = process.run(\"vgcreate %s %s\" %\n+                             (vg_name, loop_device), shell=True)\n+    except process.CmdError, ex:\n+        LOGGER.error(ex)\n+        vg_ramdisk_cleanup(ramdisk_filename, vg_ramdisk_dir,\n+                           vg_name, loop_device)\n+        raise ex\n+\n+    LOGGER.debug(result.stdout.rstrip())\n+\n+\n+def vg_ramdisk_cleanup(ramdisk_filename=None, vg_ramdisk_dir=None,\n+                       vg_name=None, loop_device=None):\n+    \"\"\"\n+    Inline cleanup function in case of test error.\n+    \"\"\"\n+    if vg_name is not None:\n+        loop_device = re.search(r\"([/w]+) %s lvm2\" % vg_name,\n+                                process.run(\"pvs\").stdout)\n+        if loop_device is not None:\n+            loop_device = loop_device.group(1)\n+        result = process.run(\"vgremove %s\" %\n+                             vg_name, ignore_status=True, shell=True)\n+        if result.exit_status == 0:\n+            LOGGER.debug(result.stdout.rstrip())\n+        else:\n+            LOGGER.debug(\"%s -> %s\", result.command, result.stderr)\n+\n+    if loop_device is not None:\n+        result = process.run(\"pvremove %s\" % loop_device,\n+                             ignore_status=True, shell=True)\n+        if result.exit_status == 0:\n+            LOGGER.debug(result.stdout.rstrip())\n+        else:\n+            LOGGER.debug(\"%s -> %s\", result.command, result.stderr)\n+\n+        if loop_device in process.run(\"losetup --all\").stdout:\n+            ramdisk_filename = re.search(r\"%s: [d+]:d+ (([/w]+))\" %\n+                                         loop_device,\n+                                         process.run(\"losetup --all\").stdout)\n+            if ramdisk_filename is not None:\n+                ramdisk_filename = ramdisk_filename.group(1)\n+\n+            for _ in range(10):\n+                time.sleep(0.1)\n+                result = process.run(\"losetup -d %s\" % loop_device,\n+                                     ignore_status=True, shell=True)\n+                if \"resource busy\" not in result.stderr:\n+                    if result.exit_status == 0:\n+                        LOGGER.debug(\"Loop device %s deleted\", loop_device)\n+                    else:\n+                        LOGGER.debug(\n+                            \"%s -> %s\", result.command, result.stderr)\n+                    break\n+\n+    if ramdisk_filename is not None:\n+        if os.path.exists(ramdisk_filename):\n+            os.unlink(ramdisk_filename)\n+            LOGGER.debug(\"Ramdisk filename %s deleted\", ramdisk_filename)\n+            vg_ramdisk_dir = os.path.dirname(ramdisk_filename)\n+\n+    if vg_ramdisk_dir is not None:\n+        process.run(\"umount %s\" % vg_ramdisk_dir,\n+                    ignore_status=True, shell=True)\n+        if result.exit_status == 0:\n+            LOGGER.debug(\"Successfully unmounted tmpfs from %s\",\n+                         vg_ramdisk_dir)\n+        else:\n+            LOGGER.debug(\"%s -> %s\", result.command, result.stderr)\n+\n+        if os.path.exists(vg_ramdisk_dir):\n+            try:\n+                shutil.rmtree(vg_ramdisk_dir)\n+                LOGGER.debug(\"Ramdisk directory %s deleted\", vg_ramdisk_dir)\n+            except OSError:\n+                pass\n+\n+\n+def vg_check(vg_name):\n+    \"\"\"\n+    Check whether provided volume group exists.\n+    \"\"\"\n+    cmd = \"vgdisplay %s\" % vg_name\n+    try:\n+        process.run(cmd, shell=True)\n+        LOGGER.debug(\"Provided volume group exists: %s\", vg_name)\n+        return True\n+    except process.CmdError, exception:\n+        LOGGER.error(exception)\n+        return False\n+\n+\n+def vg_list():\n+    \"\"\"\n+    List available volume groups.\n+    \"\"\"\n+    cmd = \"vgs --all\"\n+    vgroups = {}\n+    result = process.run(cmd, shell=True)\n+    lines = result.stdout.strip().splitlines()\n+    if len(lines) > 1:\n+        columns = lines[0].split()\n+        lines = lines[1:]\n+    else:\n+        return vgroups\n+\n+    for line in lines:\n+        details = line.split()\n+        details_dict = {}\n+        index = 0\n+        for column in columns:\n+            if re.search(\"VG\", column):\n+                vg_name = details[index]\n+            else:\n+                details_dict[column] = details[index]\n+            index += 1\n+        vgroups[vg_name] = details_dict\n+    return vgroups\n+\n+\n+def vg_create(vg_name, pv_list, force=False):\n+    \"\"\"\n+    Create a volume group by using the block special devices\n+    \"\"\"\n+\n+    if vg_check(vg_name):\n+        raise Exception(\"Volume group '%s' already exist\" % vg_name)\n+    if force:\n+        cmd = \"vgcreate -f\"\n+    else:\n+        cmd = \"vgcreate\"\n+    cmd += \" %s %s\" % (vg_name, pv_list)\n+    result = process.run(cmd, shell=True)\n+    LOGGER.debug(result.stdout.rstrip())\n+\n+\n+def vg_remove(vg_name):\n+    \"\"\"\n+    Remove a volume group.\n+    \"\"\"\n+\n+    if not vg_check(vg_name):\n+        raise Exception(\"Volume group '%s' could not be found\" % vg_name)\n+    cmd = \"vgremove -f %s\" % vg_name\n+    result = process.run(cmd, shell=True)\n+    LOGGER.debug(result.stdout.rstrip())\n+\n+\n+def lv_check(vg_name, lv_name):\n+    \"\"\"\n+    Check whether provided Logical volume exists.\n+    \"\"\"\n+    cmd = \"lvdisplay\"\n+    result = process.run(cmd, ignore_status=True, shell=True)\n+\n+    lvpattern = r\"LV Path\\s+/dev/%s/%s\\s+\" % (vg_name, lv_name)\n+    match = re.search(lvpattern, result.stdout.rstrip())\n+    if match:\n+        LOGGER.debug(\"Provided Logical volume %s exists in %s\",\n+                     lv_name, vg_name)\n+        return True\n+    else:\n+        return False\n+\n+\n+def lv_remove(vg_name, lv_name):\n+    \"\"\"\n+    Remove a logical volume.\n+    \"\"\"\n+\n+    if not vg_check(vg_name):\n+        raise Exception(\"Volume group could not be found\")\n+    if not lv_check(vg_name, lv_name):\n+        raise Exception(\"Logical volume could not be found\")\n+\n+    cmd = \"lvremove -f %s/%s\" % (vg_name, lv_name)\n+    result = process.run(cmd, shell=True)\n+    LOGGER.debug(result.stdout.rstrip())\n+\n+\n+def lv_create(vg_name, lv_name, lv_size, force_flag=True):\n+    \"\"\"\n+    Create a Logical volume in a volume group.\n+\n+    The volume group must already exist.\n+    \"\"\"\n+\n+    if not vg_check(vg_name):\n+        raise Exception(\"Volume group could not be found\")\n+    if lv_check(vg_name, lv_name) and not force_flag:\n+        raise Exception(\"Logical volume already exists\")\n+    elif lv_check(vg_name, lv_name) and force_flag:\n+        lv_remove(vg_name, lv_name)\n+\n+    cmd = \"lvcreate --size %s --name %s %s\" % (lv_size, lv_name, vg_name)\n+    result = process.run(cmd, shell=True)\n+    LOGGER.debug(result.stdout.rstrip())\n+\n+\n+def lv_list():\n+    \"\"\"\n+    List available group volumes.\n+    \"\"\"\n+    cmd = \"lvs --all\"\n+    volumes = {}\n+    result = process.run(cmd, shell=True)\n+\n+    lines = result.stdout.strip().splitlines()\n+    if len(lines) > 1:\n+        lines = lines[1:]\n+    else:\n+        return volumes\n+\n+    for line in lines:\n+        details = line.split()\n+        length = len(details)\n+        details_dict = {}\n+        lv_name = details[0]\n+        details_dict[\"VG\"] = details[1]\n+        details_dict[\"Attr\"] = details[2]\n+        details_dict[\"LSize\"] = details[3]\n+        if length == 5:\n+            details_dict[\"Origin_Data\"] = details[4]\n+        elif length > 5:\n+            details_dict[\"Origin_Data\"] = details[5]\n+            details_dict[\"Pool\"] = details[4]\n+        volumes[lv_name] = details_dict\n+    return volumes\n+\n+\n+def thin_lv_create(vg_name, thinpool_name=\"lvthinpool\", thinpool_size=\"1.5G\",\n+                   thinlv_name=\"lvthin\", thinlv_size=\"1G\"):\n+    \"\"\"\n+    Create a thin volume from given volume group.\n+\n+    :param vg_name: An exist volume group\n+    :param thinpool_name: The name of thin pool\n+    :param thinpool_size: The size of thin pool to be created\n+    :param thinlv_name: The name of thin volume\n+    :param thinlv_size: The size of thin volume\n+    \"\"\"\n+    tp_cmd = \"lvcreate --thinpool %s --size %s %s\" % (thinpool_name,\n+                                                      thinpool_size,\n+                                                      vg_name)\n+    try:\n+        process.run(tp_cmd, shell=True)\n+    except process.CmdError, detail:\n+        LOGGER.debug(detail)\n+        raise Exception(\"Create thin volume pool failed.\")\n+    LOGGER.debug(\"Created thin volume pool: %s\", thinpool_name)\n+    lv_cmd = (\"lvcreate --name %s --virtualsize %s \"\n+              \"--thin %s/%s\" % (thinlv_name, thinlv_size,\n+                                vg_name, thinpool_name))\n+    try:\n+        process.run(lv_cmd, shell=True)\n+    except process.CmdError, detail:\n+        LOGGER.error(detail)\n+        raise Exception(\"Create thin volume failed.\")\n+    LOGGER.debug(\"Created thin volume:%s\", thinlv_name)\n+    return (thinpool_name, thinlv_name)\n+\n+\n+def lv_take_snapshot(vg_name, lv_name,\n+                     lv_snapshot_name, lv_snapshot_size):\n+    \"\"\"\n+    Take a snapshot of the original Logical volume.\n+    \"\"\"\n+\n+    if not vg_check(vg_name):\n+        raise Exception(\"Volume group could not be found\")\n+    if lv_check(vg_name, lv_snapshot_name):\n+        raise Exception(\"Snapshot already exists\")\n+    if not lv_check(vg_name, lv_name):\n+        raise Exception(\"Snapshot's origin could not be found\")\n+\n+    cmd = (\"lvcreate --size %s --snapshot --name %s /dev/%s/%s\" %\n+           (lv_snapshot_size, lv_snapshot_name, vg_name, lv_name))\n+    try:\n+        result = process.run(cmd, shell=True)\n+    except process.CmdError, ex:\n+        if ('Logical volume \"%s\" already exists in volume group \"%s\"' %\n+            (lv_snapshot_name, vg_name) in ex.result_obj.stderr and\n+            re.search(re.escape(lv_snapshot_name + \" [active]\"),\n+                      process.run(\"lvdisplay\").stdout)):\n+            # the above conditions detect if merge of snapshot was postponed\n+            LOGGER.debug((\"Logical volume %s is still active! \" +\n+                          \"Attempting to deactivate...\"), lv_name)\n+            lv_reactivate(vg_name, lv_name)\n+            result = process.run(cmd, shell=True)\n+        else:\n+            raise ex\n+    LOGGER.info(result.stdout.rstrip())\n+\n+\n+def lv_revert(vg_name, lv_name, lv_snapshot_name):\n+    \"\"\"\n+    Revert the origin to a snapshot.\n+    \"\"\"\n+    try:\n+        if not vg_check(vg_name):\n+            raise Exception(\"Volume group could not be found\")\n+        if not lv_check(vg_name, lv_snapshot_name):\n+            raise Exception(\"Snapshot could not be found\")\n+        if (not lv_check(vg_name, lv_snapshot_name) and not lv_check(vg_name,\n+                                                                     lv_name)):\n+            raise Exception(\"Snapshot and its origin could not be found\")\n+        if (lv_check(vg_name, lv_snapshot_name) and not lv_check(vg_name,\n+                                                                 lv_name)):\n+            raise Exception(\"Snapshot origin could not be found\")\n+\n+        cmd = (\"lvconvert --merge /dev/%s/%s\" % (vg_name, lv_snapshot_name))\n+        result = process.run(cmd, shell=True)\n+        if ((\"Merging of snapshot %s will start next activation.\" %\n+             lv_snapshot_name) in result.stdout):\n+            raise Exception(\"The Logical volume %s is still active\" %\n+                            lv_name)\n+        result = result.stdout.rstrip()\n+\n+    except Exception, ex:\n+        # detect if merge of snapshot was postponed\n+        # and attempt to reactivate the volume.\n+        active_lv_pattern = re.escape(\"%s [active]\" % lv_snapshot_name)\n+        lvdisplay_output = process.run(\"lvdisplay\").stdout\n+        if ('Snapshot could not be found' in ex and\n+                re.search(active_lv_pattern, lvdisplay_output) or\n+                \"The Logical volume %s is still active\" % lv_name in ex):\n+            LOGGER.debug((\"Logical volume %s is still active! \" +\n+                          \"Attempting to deactivate...\"), lv_name)\n+            lv_reactivate(vg_name, lv_name)\n+            result = \"Continuing after reactivation\"\n+        elif 'Snapshot could not be found' in ex:\n+            LOGGER.error(ex)\n+            result = \"Could not revert to snapshot\"\n+        else:\n+            raise ex\n+    LOGGER.debug(result)\n+\n+\n+def lv_revert_with_snapshot(vg_name, lv_name,\n+                            lv_snapshot_name, lv_snapshot_size):\n+    \"\"\"\n+    Perform Logical volume merge with snapshot and take a new snapshot.\n+    \"\"\"\n+\n+    lv_revert(vg_name, lv_name, lv_snapshot_name)\n+    lv_take_snapshot(vg_name, lv_name, lv_snapshot_name, lv_snapshot_size)\n+\n+\n+def lv_reactivate(vg_name, lv_name, timeout=10):\n+    \"\"\"\n+    In case of unclean shutdowns some of the lvs is still active and merging\n+    is postponed. Use this function to attempt to deactivate and reactivate\n+    all of them to cause the merge to happen.\n+    \"\"\"\n+    try:\n+        process.run(\"lvchange -an /dev/%s/%s\" % (vg_name, lv_name), shell=True)\n+        time.sleep(timeout)\n+        process.run(\"lvchange -ay /dev/%s/%s\" % (vg_name, lv_name), shell=True)\n+        time.sleep(timeout)\n+    except process.CmdError:\n+        LOGGER.error((\"Failed to reactivate %s - please, \" +\n+                      \"nuke the process that uses it first.\"), lv_name)\n+        raise Exception(\"The Logical volume %s is still active\" % lv_name)\n+\n+\n+def lv_mount(vg_name, lv_name, mount_loc, create_filesystem=\"\"):\n+    \"\"\"\n+    Mount a Logical volume to a mount location.\n+\n+    The create_filesystem can be one of ext2, ext3, ext4, vfat or empty\n+    if the filesystem was already created and the mkfs process is skipped.\n+    \"\"\"\n+\n+    try:\n+        if create_filesystem:\n+            result = process.run(\"mkfs.%s /dev/%s/%s\" %\n+                                 (create_filesystem, vg_name, lv_name),\n+                                 shell=True)","path":"avocado/utils/lv_utils.py","position":497,"original_position":497,"commit_id":"23640138c9a8f9bcef4b1d7b1ceaeeea3fc1dd8d","original_commit_id":"23640138c9a8f9bcef4b1d7b1ceaeeea3fc1dd8d","user":{"login":"ldoktor","id":1074021,"avatar_url":"https://avatars.githubusercontent.com/u/1074021?v=3","gravatar_id":"","url":"https://api.github.com/users/ldoktor","html_url":"https://github.com/ldoktor","followers_url":"https://api.github.com/users/ldoktor/followers","following_url":"https://api.github.com/users/ldoktor/following{/other_user}","gists_url":"https://api.github.com/users/ldoktor/gists{/gist_id}","starred_url":"https://api.github.com/users/ldoktor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldoktor/subscriptions","organizations_url":"https://api.github.com/users/ldoktor/orgs","repos_url":"https://api.github.com/users/ldoktor/repos","events_url":"https://api.github.com/users/ldoktor/events{/privacy}","received_events_url":"https://api.github.com/users/ldoktor/received_events","type":"User","site_admin":false},"body":"shell=False, sudo=True","created_at":"2016-06-08T12:14:14Z","updated_at":"2016-06-08T12:14:14Z","html_url":"https://github.com/avocado-framework/avocado/pull/1237#discussion_r66243640","pull_request_url":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237","_links":{"self":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/comments/66243640"},"html":{"href":"https://github.com/avocado-framework/avocado/pull/1237#discussion_r66243640"},"pull_request":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237"}}},"pull_request":{"url":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237","id":71855302,"html_url":"https://github.com/avocado-framework/avocado/pull/1237","diff_url":"https://github.com/avocado-framework/avocado/pull/1237.diff","patch_url":"https://github.com/avocado-framework/avocado/pull/1237.patch","issue_url":"https://api.github.com/repos/avocado-framework/avocado/issues/1237","number":1237,"state":"open","locked":false,"title":"Adding lv_utils for lvsetup Test in avocado","user":{"login":"harish-24","id":17269437,"avatar_url":"https://avatars.githubusercontent.com/u/17269437?v=3","gravatar_id":"","url":"https://api.github.com/users/harish-24","html_url":"https://github.com/harish-24","followers_url":"https://api.github.com/users/harish-24/followers","following_url":"https://api.github.com/users/harish-24/following{/other_user}","gists_url":"https://api.github.com/users/harish-24/gists{/gist_id}","starred_url":"https://api.github.com/users/harish-24/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/harish-24/subscriptions","organizations_url":"https://api.github.com/users/harish-24/orgs","repos_url":"https://api.github.com/users/harish-24/repos","events_url":"https://api.github.com/users/harish-24/events{/privacy}","received_events_url":"https://api.github.com/users/harish-24/received_events","type":"User","site_admin":false},"body":"This utility is used in Lvsetup test, which automatically takes snapshots from existing logical volumes\r\nor creates them in a loop disk or specified disk.","created_at":"2016-05-30T05:51:21Z","updated_at":"2016-06-08T12:14:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c48b9b893936c0caed74bc03793814a51beb8228","assignee":{"login":"ldoktor","id":1074021,"avatar_url":"https://avatars.githubusercontent.com/u/1074021?v=3","gravatar_id":"","url":"https://api.github.com/users/ldoktor","html_url":"https://github.com/ldoktor","followers_url":"https://api.github.com/users/ldoktor/followers","following_url":"https://api.github.com/users/ldoktor/following{/other_user}","gists_url":"https://api.github.com/users/ldoktor/gists{/gist_id}","starred_url":"https://api.github.com/users/ldoktor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldoktor/subscriptions","organizations_url":"https://api.github.com/users/ldoktor/orgs","repos_url":"https://api.github.com/users/ldoktor/repos","events_url":"https://api.github.com/users/ldoktor/events{/privacy}","received_events_url":"https://api.github.com/users/ldoktor/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237/commits","review_comments_url":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237/comments","review_comment_url":"https://api.github.com/repos/avocado-framework/avocado/pulls/comments{/number}","comments_url":"https://api.github.com/repos/avocado-framework/avocado/issues/1237/comments","statuses_url":"https://api.github.com/repos/avocado-framework/avocado/statuses/23640138c9a8f9bcef4b1d7b1ceaeeea3fc1dd8d","head":{"label":"harish-24:lv_utils","ref":"lv_utils","sha":"23640138c9a8f9bcef4b1d7b1ceaeeea3fc1dd8d","user":{"login":"harish-24","id":17269437,"avatar_url":"https://avatars.githubusercontent.com/u/17269437?v=3","gravatar_id":"","url":"https://api.github.com/users/harish-24","html_url":"https://github.com/harish-24","followers_url":"https://api.github.com/users/harish-24/followers","following_url":"https://api.github.com/users/harish-24/following{/other_user}","gists_url":"https://api.github.com/users/harish-24/gists{/gist_id}","starred_url":"https://api.github.com/users/harish-24/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/harish-24/subscriptions","organizations_url":"https://api.github.com/users/harish-24/orgs","repos_url":"https://api.github.com/users/harish-24/repos","events_url":"https://api.github.com/users/harish-24/events{/privacy}","received_events_url":"https://api.github.com/users/harish-24/received_events","type":"User","site_admin":false},"repo":{"id":59107309,"name":"avocado","full_name":"harish-24/avocado","owner":{"login":"harish-24","id":17269437,"avatar_url":"https://avatars.githubusercontent.com/u/17269437?v=3","gravatar_id":"","url":"https://api.github.com/users/harish-24","html_url":"https://github.com/harish-24","followers_url":"https://api.github.com/users/harish-24/followers","following_url":"https://api.github.com/users/harish-24/following{/other_user}","gists_url":"https://api.github.com/users/harish-24/gists{/gist_id}","starred_url":"https://api.github.com/users/harish-24/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/harish-24/subscriptions","organizations_url":"https://api.github.com/users/harish-24/orgs","repos_url":"https://api.github.com/users/harish-24/repos","events_url":"https://api.github.com/users/harish-24/events{/privacy}","received_events_url":"https://api.github.com/users/harish-24/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/harish-24/avocado","description":"Avocado Test Framework","fork":true,"url":"https://api.github.com/repos/harish-24/avocado","forks_url":"https://api.github.com/repos/harish-24/avocado/forks","keys_url":"https://api.github.com/repos/harish-24/avocado/keys{/key_id}","collaborators_url":"https://api.github.com/repos/harish-24/avocado/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/harish-24/avocado/teams","hooks_url":"https://api.github.com/repos/harish-24/avocado/hooks","issue_events_url":"https://api.github.com/repos/harish-24/avocado/issues/events{/number}","events_url":"https://api.github.com/repos/harish-24/avocado/events","assignees_url":"https://api.github.com/repos/harish-24/avocado/assignees{/user}","branches_url":"https://api.github.com/repos/harish-24/avocado/branches{/branch}","tags_url":"https://api.github.com/repos/harish-24/avocado/tags","blobs_url":"https://api.github.com/repos/harish-24/avocado/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/harish-24/avocado/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/harish-24/avocado/git/refs{/sha}","trees_url":"https://api.github.com/repos/harish-24/avocado/git/trees{/sha}","statuses_url":"https://api.github.com/repos/harish-24/avocado/statuses/{sha}","languages_url":"https://api.github.com/repos/harish-24/avocado/languages","stargazers_url":"https://api.github.com/repos/harish-24/avocado/stargazers","contributors_url":"https://api.github.com/repos/harish-24/avocado/contributors","subscribers_url":"https://api.github.com/repos/harish-24/avocado/subscribers","subscription_url":"https://api.github.com/repos/harish-24/avocado/subscription","commits_url":"https://api.github.com/repos/harish-24/avocado/commits{/sha}","git_commits_url":"https://api.github.com/repos/harish-24/avocado/git/commits{/sha}","comments_url":"https://api.github.com/repos/harish-24/avocado/comments{/number}","issue_comment_url":"https://api.github.com/repos/harish-24/avocado/issues/comments{/number}","contents_url":"https://api.github.com/repos/harish-24/avocado/contents/{+path}","compare_url":"https://api.github.com/repos/harish-24/avocado/compare/{base}...{head}","merges_url":"https://api.github.com/repos/harish-24/avocado/merges","archive_url":"https://api.github.com/repos/harish-24/avocado/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/harish-24/avocado/downloads","issues_url":"https://api.github.com/repos/harish-24/avocado/issues{/number}","pulls_url":"https://api.github.com/repos/harish-24/avocado/pulls{/number}","milestones_url":"https://api.github.com/repos/harish-24/avocado/milestones{/number}","notifications_url":"https://api.github.com/repos/harish-24/avocado/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/harish-24/avocado/labels{/name}","releases_url":"https://api.github.com/repos/harish-24/avocado/releases{/id}","deployments_url":"https://api.github.com/repos/harish-24/avocado/deployments","created_at":"2016-05-18T10:53:11Z","updated_at":"2016-05-18T10:53:13Z","pushed_at":"2016-06-03T07:39:46Z","git_url":"git://github.com/harish-24/avocado.git","ssh_url":"git@github.com:harish-24/avocado.git","clone_url":"https://github.com/harish-24/avocado.git","svn_url":"https://github.com/harish-24/avocado","homepage":"http://avocado-framework.github.io/","size":8648,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"avocado-framework:master","ref":"master","sha":"ff1bdca88623f439ee57cf15acb78957a53ec235","user":{"login":"avocado-framework","id":7245719,"avatar_url":"https://avatars.githubusercontent.com/u/7245719?v=3","gravatar_id":"","url":"https://api.github.com/users/avocado-framework","html_url":"https://github.com/avocado-framework","followers_url":"https://api.github.com/users/avocado-framework/followers","following_url":"https://api.github.com/users/avocado-framework/following{/other_user}","gists_url":"https://api.github.com/users/avocado-framework/gists{/gist_id}","starred_url":"https://api.github.com/users/avocado-framework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avocado-framework/subscriptions","organizations_url":"https://api.github.com/users/avocado-framework/orgs","repos_url":"https://api.github.com/users/avocado-framework/repos","events_url":"https://api.github.com/users/avocado-framework/events{/privacy}","received_events_url":"https://api.github.com/users/avocado-framework/received_events","type":"Organization","site_admin":false},"repo":{"id":17449498,"name":"avocado","full_name":"avocado-framework/avocado","owner":{"login":"avocado-framework","id":7245719,"avatar_url":"https://avatars.githubusercontent.com/u/7245719?v=3","gravatar_id":"","url":"https://api.github.com/users/avocado-framework","html_url":"https://github.com/avocado-framework","followers_url":"https://api.github.com/users/avocado-framework/followers","following_url":"https://api.github.com/users/avocado-framework/following{/other_user}","gists_url":"https://api.github.com/users/avocado-framework/gists{/gist_id}","starred_url":"https://api.github.com/users/avocado-framework/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/avocado-framework/subscriptions","organizations_url":"https://api.github.com/users/avocado-framework/orgs","repos_url":"https://api.github.com/users/avocado-framework/repos","events_url":"https://api.github.com/users/avocado-framework/events{/privacy}","received_events_url":"https://api.github.com/users/avocado-framework/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/avocado-framework/avocado","description":"Avocado Test Framework","fork":false,"url":"https://api.github.com/repos/avocado-framework/avocado","forks_url":"https://api.github.com/repos/avocado-framework/avocado/forks","keys_url":"https://api.github.com/repos/avocado-framework/avocado/keys{/key_id}","collaborators_url":"https://api.github.com/repos/avocado-framework/avocado/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/avocado-framework/avocado/teams","hooks_url":"https://api.github.com/repos/avocado-framework/avocado/hooks","issue_events_url":"https://api.github.com/repos/avocado-framework/avocado/issues/events{/number}","events_url":"https://api.github.com/repos/avocado-framework/avocado/events","assignees_url":"https://api.github.com/repos/avocado-framework/avocado/assignees{/user}","branches_url":"https://api.github.com/repos/avocado-framework/avocado/branches{/branch}","tags_url":"https://api.github.com/repos/avocado-framework/avocado/tags","blobs_url":"https://api.github.com/repos/avocado-framework/avocado/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/avocado-framework/avocado/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/avocado-framework/avocado/git/refs{/sha}","trees_url":"https://api.github.com/repos/avocado-framework/avocado/git/trees{/sha}","statuses_url":"https://api.github.com/repos/avocado-framework/avocado/statuses/{sha}","languages_url":"https://api.github.com/repos/avocado-framework/avocado/languages","stargazers_url":"https://api.github.com/repos/avocado-framework/avocado/stargazers","contributors_url":"https://api.github.com/repos/avocado-framework/avocado/contributors","subscribers_url":"https://api.github.com/repos/avocado-framework/avocado/subscribers","subscription_url":"https://api.github.com/repos/avocado-framework/avocado/subscription","commits_url":"https://api.github.com/repos/avocado-framework/avocado/commits{/sha}","git_commits_url":"https://api.github.com/repos/avocado-framework/avocado/git/commits{/sha}","comments_url":"https://api.github.com/repos/avocado-framework/avocado/comments{/number}","issue_comment_url":"https://api.github.com/repos/avocado-framework/avocado/issues/comments{/number}","contents_url":"https://api.github.com/repos/avocado-framework/avocado/contents/{+path}","compare_url":"https://api.github.com/repos/avocado-framework/avocado/compare/{base}...{head}","merges_url":"https://api.github.com/repos/avocado-framework/avocado/merges","archive_url":"https://api.github.com/repos/avocado-framework/avocado/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/avocado-framework/avocado/downloads","issues_url":"https://api.github.com/repos/avocado-framework/avocado/issues{/number}","pulls_url":"https://api.github.com/repos/avocado-framework/avocado/pulls{/number}","milestones_url":"https://api.github.com/repos/avocado-framework/avocado/milestones{/number}","notifications_url":"https://api.github.com/repos/avocado-framework/avocado/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/avocado-framework/avocado/labels{/name}","releases_url":"https://api.github.com/repos/avocado-framework/avocado/releases{/id}","deployments_url":"https://api.github.com/repos/avocado-framework/avocado/deployments","created_at":"2014-03-05T17:52:27Z","updated_at":"2016-06-08T08:57:44Z","pushed_at":"2016-06-08T11:03:57Z","git_url":"git://github.com/avocado-framework/avocado.git","ssh_url":"git@github.com:avocado-framework/avocado.git","clone_url":"https://github.com/avocado-framework/avocado.git","svn_url":"https://github.com/avocado-framework/avocado","homepage":"http://avocado-framework.github.io/","size":13795,"stargazers_count":41,"watchers_count":41,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":59,"mirror_url":null,"open_issues_count":13,"forks":59,"open_issues":13,"watchers":41,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237"},"html":{"href":"https://github.com/avocado-framework/avocado/pull/1237"},"issue":{"href":"https://api.github.com/repos/avocado-framework/avocado/issues/1237"},"comments":{"href":"https://api.github.com/repos/avocado-framework/avocado/issues/1237/comments"},"review_comments":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237/comments"},"review_comment":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/avocado-framework/avocado/pulls/1237/commits"},"statuses":{"href":"https://api.github.com/repos/avocado-framework/avocado/statuses/23640138c9a8f9bcef4b1d7b1ceaeeea3fc1dd8d"}}}}
{"actor":{"display_login":"ldoktor"}}