{"action":"created","comment":{"url":"https://api.github.com/repos/collectd/collectd/pulls/comments/78736664","id":78736664,"diff_hunk":"@@ -0,0 +1,775 @@\n+/*-\n+ * collectd - src/dpdkstat.c\n+ * MIT License\n+ *\n+ * Copyright(c) 2016 Intel Corporation. All rights reserved.\n+ *\n+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\n+ * this software and associated documentation files (the \"Software\"), to deal in\n+ * the Software without restriction, including without limitation the rights to\n+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n+ * of the Software, and to permit persons to whom the Software is furnished to do\n+ * so, subject to the following conditions:\n+ *\n+ * The above copyright notice and this permission notice shall be included in all\n+ * copies or substantial portions of the Software.\n+ *\n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n+ * SOFTWARE.\n+ *\n+ * Authors:\n+ *   Maryam Tahhan <maryam.tahhan@intel.com>\n+ *   Harry van Haaren <harry.van.haaren@intel.com>\n+ */\n+\n+#include \"collectd.h\"\n+#include \"common.h\" /* auxiliary functions */\n+#include \"plugin.h\" /* plugin_register_*, plugin_dispatch_values */\n+#include \"utils_time.h\"\n+\n+#include <getopt.h>\n+#include <semaphore.h>\n+#include <sys/mman.h>\n+#include <sys/queue.h>\n+#include <poll.h>\n+\n+#include <rte_config.h>\n+#include <rte_eal.h>\n+#include <rte_ethdev.h>\n+#include <rte_common.h>\n+#include <rte_debug.h>\n+#include <rte_malloc.h>\n+#include <rte_memory.h>\n+#include <rte_memzone.h>\n+#include <rte_launch.h>\n+#include <rte_tailq.h>\n+#include <rte_lcore.h>\n+#include <rte_per_lcore.h>\n+#include <rte_debug.h>\n+#include <rte_log.h>\n+#include <rte_atomic.h>\n+#include <rte_branch_prediction.h>\n+#include <rte_string_fns.h>\n+\n+#define DPDKSTAT_MAX_BUFFER_SIZE (4096*4)\n+#define DPDK_SHM_NAME \"dpdk_collectd_stats_shm\"\n+#define REINIT_SHM 1\n+#define RESET 1\n+#define NO_RESET 0\n+\n+enum DPDK_HELPER_ACTION {\n+  DPDK_HELPER_ACTION_COUNT_STATS,\n+  DPDK_HELPER_ACTION_SEND_STATS,\n+};\n+\n+enum DPDK_HELPER_STATUS {\n+  DPDK_HELPER_NOT_INITIALIZED = 0,\n+  DPDK_HELPER_WAITING_ON_PRIMARY,\n+  DPDK_HELPER_INITIALIZING_EAL,\n+  DPDK_HELPER_ALIVE_SENDING_STATS,\n+  DPDK_HELPER_GRACEFUL_QUIT,\n+};\n+\n+struct dpdk_config_s {\n+  /* General DPDK params */\n+  char coremask[DATA_MAX_NAME_LEN];\n+  char memory_channels[DATA_MAX_NAME_LEN];\n+  char socket_memory[DATA_MAX_NAME_LEN];\n+  char process_type[DATA_MAX_NAME_LEN];\n+  char file_prefix[DATA_MAX_NAME_LEN];\n+  cdtime_t interval;\n+  uint32_t eal_initialized;\n+  uint32_t enabled_port_mask;\n+  char port_name[RTE_MAX_ETHPORTS][DATA_MAX_NAME_LEN];\n+  uint32_t eal_argc;\n+  /* Helper info */\n+  int   collectd_reinit_shm;\n+  pid_t helper_pid;\n+  sem_t sema_helper_get_stats;\n+  sem_t sema_stats_in_shm;\n+  int   helper_pipes[2];\n+  enum DPDK_HELPER_STATUS helper_status;\n+  enum DPDK_HELPER_ACTION helper_action;\n+  /* xstats info */\n+  uint32_t num_ports;\n+  uint32_t num_xstats;\n+  cdtime_t port_read_time[RTE_MAX_ETHPORTS];\n+  uint32_t num_stats_in_port[RTE_MAX_ETHPORTS];\n+  struct rte_eth_link link_status[RTE_MAX_ETHPORTS];\n+  struct rte_eth_xstats xstats;\n+  /* rte_eth_xstats from here on until the end of the SHM */\n+};\n+typedef struct dpdk_config_s dpdk_config_t;\n+\n+static int g_configured;\n+static dpdk_config_t *g_configuration;\n+\n+static void dpdk_config_init_default(void);\n+static int dpdk_config(oconfig_item_t *ci);\n+static int dpdk_helper_init_eal(void);\n+static int dpdk_helper_run(void);\n+static int dpdk_helper_spawn(enum DPDK_HELPER_ACTION action);\n+static int dpdk_init(void);\n+static int dpdk_read(user_data_t *ud);\n+static int dpdk_shm_cleanup(void);\n+static int dpdk_shm_init(size_t size);\n+\n+/* Write the default configuration to the g_configuration instances */\n+static void dpdk_config_init_default(void)\n+{\n+    g_configuration->interval = plugin_get_interval();\n+    WARNING(\"dpdkstat: No time interval was configured, default value %lu ms is set\\n\",\n+             CDTIME_T_TO_MS(g_configuration->interval));\n+    /* Default is all ports enabled */\n+    g_configuration->enabled_port_mask = ~0;\n+    g_configuration->eal_argc = 2;\n+    g_configuration->eal_initialized = 0;\n+    ssnprintf(g_configuration->coremask, DATA_MAX_NAME_LEN, \"%s\", \"0xf\");\n+    ssnprintf(g_configuration->memory_channels, DATA_MAX_NAME_LEN, \"%s\", \"1\");\n+    ssnprintf(g_configuration->process_type, DATA_MAX_NAME_LEN, \"%s\", \"secondary\");\n+    ssnprintf(g_configuration->file_prefix, DATA_MAX_NAME_LEN, \"%s\",\n+             \"/var/run/.rte_config\");\n+\n+    for (int i = 0; i < RTE_MAX_ETHPORTS; i++)\n+      g_configuration->port_name[i][0] = 0;\n+}\n+\n+static int dpdk_config(oconfig_item_t *ci)\n+{\n+  int port_counter = 0;\n+\n+  /* Initialize a POSIX SHared Memory (SHM) object. */\n+  int err = dpdk_shm_init(sizeof(dpdk_config_t));\n+  if (err) {\n+    DEBUG(\"dpdkstat: error in shm_init, %s\", strerror(errno));\n+    return -1;\n+  }\n+\n+  /* Set defaults for config, overwritten by loop if config item exists */\n+  dpdk_config_init_default();\n+\n+  for (int i = 0; i < ci->children_num; i++) {\n+    oconfig_item_t *child = ci->children + i;\n+\n+    if (strcasecmp(\"Interval\", child->key) == 0) {\n+      g_configuration->interval =\n+            DOUBLE_TO_CDTIME_T (child->values[0].value.number);\n+      DEBUG(\"dpdkstat: Plugin Read Interval %lu milliseconds\\n\",\n+            CDTIME_T_TO_MS(g_configuration->interval));\n+    } else if (strcasecmp(\"Coremask\", child->key) == 0) {\n+      ssnprintf(g_configuration->coremask, DATA_MAX_NAME_LEN, \"%s\",\n+               child->values[0].value.string);\n+      DEBUG(\"dpdkstat:COREMASK %s \\n\", g_configuration->coremask);\n+      g_configuration->eal_argc+=1;\n+    } else if (strcasecmp(\"MemoryChannels\", child->key) == 0) {\n+      ssnprintf(g_configuration->memory_channels, DATA_MAX_NAME_LEN, \"%s\",\n+               child->values[0].value.string);\n+      DEBUG(\"dpdkstat:Memory Channels %s \\n\", g_configuration->memory_channels);\n+      g_configuration->eal_argc+=1;\n+    } else if (strcasecmp(\"SocketMemory\", child->key) == 0) {\n+      ssnprintf(g_configuration->socket_memory, DATA_MAX_NAME_LEN, \"%s\",\n+               child->values[0].value.string);\n+      DEBUG(\"dpdkstat: socket mem %s \\n\", g_configuration->socket_memory);\n+      g_configuration->eal_argc+=1;\n+    } else if (strcasecmp(\"ProcessType\", child->key) == 0) {\n+      ssnprintf(g_configuration->process_type, DATA_MAX_NAME_LEN, \"%s\",\n+               child->values[0].value.string);\n+      DEBUG(\"dpdkstat: proc type %s \\n\", g_configuration->process_type);\n+      g_configuration->eal_argc+=1;\n+    } else if (strcasecmp(\"FilePrefix\", child->key) == 0) {\n+      ssnprintf(g_configuration->file_prefix, DATA_MAX_NAME_LEN, \"/var/run/.%s_config\",\n+               child->values[0].value.string);\n+      DEBUG(\"dpdkstat: file prefix %s \\n\", g_configuration->file_prefix);\n+      if (strcasecmp(g_configuration->file_prefix, \"/var/run/.rte_config\") != 0) {\n+        g_configuration->eal_argc+=1;\n+      }\n+    } else if (strcasecmp(\"EnabledPortMask\", child->key) == 0) {\n+      g_configuration->enabled_port_mask = (uint32_t)child->values[0].value.number;","path":"src/dpdkstat.c","position":192,"original_position":192,"commit_id":"acbf4e5d5b848540c6fc0d9bfb235f6441564384","original_commit_id":"acbf4e5d5b848540c6fc0d9bfb235f6441564384","user":{"login":"octo","id":116087,"avatar_url":"https://avatars.githubusercontent.com/u/116087?v=3","gravatar_id":"","url":"https://api.github.com/users/octo","html_url":"https://github.com/octo","followers_url":"https://api.github.com/users/octo/followers","following_url":"https://api.github.com/users/octo/following{/other_user}","gists_url":"https://api.github.com/users/octo/gists{/gist_id}","starred_url":"https://api.github.com/users/octo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/octo/subscriptions","organizations_url":"https://api.github.com/users/octo/orgs","repos_url":"https://api.github.com/users/octo/repos","events_url":"https://api.github.com/users/octo/events{/privacy}","received_events_url":"https://api.github.com/users/octo/received_events","type":"User","site_admin":false},"body":"Please check that the config value is of the right type before accessing the union:\r\n\r\n```c\r\nif (child->values[0].type == OCONFIG_TYPE_NUMBER)\r\n  …\r\n```","created_at":"2016-09-14T12:18:50Z","updated_at":"2016-09-14T12:18:50Z","html_url":"https://github.com/collectd/collectd/pull/1649#discussion_r78736664","pull_request_url":"https://api.github.com/repos/collectd/collectd/pulls/1649","_links":{"self":{"href":"https://api.github.com/repos/collectd/collectd/pulls/comments/78736664"},"html":{"href":"https://github.com/collectd/collectd/pull/1649#discussion_r78736664"},"pull_request":{"href":"https://api.github.com/repos/collectd/collectd/pulls/1649"}}},"pull_request":{"url":"https://api.github.com/repos/collectd/collectd/pulls/1649","id":65036798,"html_url":"https://github.com/collectd/collectd/pull/1649","diff_url":"https://github.com/collectd/collectd/pull/1649.diff","patch_url":"https://github.com/collectd/collectd/pull/1649.patch","issue_url":"https://api.github.com/repos/collectd/collectd/issues/1649","number":1649,"state":"open","locked":false,"title":"dpdkstat: enable a plugin for DPDK stats","user":{"login":"maryamtahhan","id":5012288,"avatar_url":"https://avatars.githubusercontent.com/u/5012288?v=3","gravatar_id":"","url":"https://api.github.com/users/maryamtahhan","html_url":"https://github.com/maryamtahhan","followers_url":"https://api.github.com/users/maryamtahhan/followers","following_url":"https://api.github.com/users/maryamtahhan/following{/other_user}","gists_url":"https://api.github.com/users/maryamtahhan/gists{/gist_id}","starred_url":"https://api.github.com/users/maryamtahhan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maryamtahhan/subscriptions","organizations_url":"https://api.github.com/users/maryamtahhan/orgs","repos_url":"https://api.github.com/users/maryamtahhan/repos","events_url":"https://api.github.com/users/maryamtahhan/events{/privacy}","received_events_url":"https://api.github.com/users/maryamtahhan/received_events","type":"User","site_admin":false},"body":"This patch enables support to retrieve statistics for DPDK interfaces. An overview of the threading is as follows:\r\n\r\n1.  collectd `init()` or `read()` calls `dpdk_helper_spawn()` to retrieve the required size of xstats to allocate.\r\n    1.  DPDK counts ports, stats and length, writing them to shared-memory (SHM).\r\n    2.  DPDK helper (secondary) process quits, allowing cleanup of the shared memory.\r\n2.  collectd resizes shared-memory to size of stats as provided by DPDK.\r\n3.  collectd respawns the DPDK helper.\r\n    1. Helper blocks on a semaphore until told to read the stats from DPDK and write them to SHM.\r\n4.  collectd dispatches statistics\r\n    1.  Thread blocks on semaphore until stats are available.\r\n\r\nThis threading model is required to allow the plugin to detect when the DPDK primary process/application has been killed/reset, and to avoid the plugin from stopping another DPDK primary process from starting. Some extra housekeeping is in place to ensure collectd is never stalled by using `sem_timedwait()` with a timeout. If collectd dies, the helper process will automatically quit after a timeout, as it detects its ppid has changed.\r\n\r\nSigned-off-by: Harry van Haaren <harry.van.haaren@intel.com>\r\nSigned-off-by: Maryam Tahhan <maryam.tahhan@intel.com>","created_at":"2016-04-02T12:31:42Z","updated_at":"2016-09-14T12:18:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"bdd48a97db9d7fec1c0bb2883bd981f92d99e8f1","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/collectd/collectd/pulls/1649/commits","review_comments_url":"https://api.github.com/repos/collectd/collectd/pulls/1649/comments","review_comment_url":"https://api.github.com/repos/collectd/collectd/pulls/comments{/number}","comments_url":"https://api.github.com/repos/collectd/collectd/issues/1649/comments","statuses_url":"https://api.github.com/repos/collectd/collectd/statuses/acbf4e5d5b848540c6fc0d9bfb235f6441564384","head":{"label":"maryamtahhan:dpdkstat","ref":"dpdkstat","sha":"acbf4e5d5b848540c6fc0d9bfb235f6441564384","user":{"login":"maryamtahhan","id":5012288,"avatar_url":"https://avatars.githubusercontent.com/u/5012288?v=3","gravatar_id":"","url":"https://api.github.com/users/maryamtahhan","html_url":"https://github.com/maryamtahhan","followers_url":"https://api.github.com/users/maryamtahhan/followers","following_url":"https://api.github.com/users/maryamtahhan/following{/other_user}","gists_url":"https://api.github.com/users/maryamtahhan/gists{/gist_id}","starred_url":"https://api.github.com/users/maryamtahhan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maryamtahhan/subscriptions","organizations_url":"https://api.github.com/users/maryamtahhan/orgs","repos_url":"https://api.github.com/users/maryamtahhan/repos","events_url":"https://api.github.com/users/maryamtahhan/events{/privacy}","received_events_url":"https://api.github.com/users/maryamtahhan/received_events","type":"User","site_admin":false},"repo":{"id":55240614,"name":"collectd","full_name":"maryamtahhan/collectd","owner":{"login":"maryamtahhan","id":5012288,"avatar_url":"https://avatars.githubusercontent.com/u/5012288?v=3","gravatar_id":"","url":"https://api.github.com/users/maryamtahhan","html_url":"https://github.com/maryamtahhan","followers_url":"https://api.github.com/users/maryamtahhan/followers","following_url":"https://api.github.com/users/maryamtahhan/following{/other_user}","gists_url":"https://api.github.com/users/maryamtahhan/gists{/gist_id}","starred_url":"https://api.github.com/users/maryamtahhan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/maryamtahhan/subscriptions","organizations_url":"https://api.github.com/users/maryamtahhan/orgs","repos_url":"https://api.github.com/users/maryamtahhan/repos","events_url":"https://api.github.com/users/maryamtahhan/events{/privacy}","received_events_url":"https://api.github.com/users/maryamtahhan/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/maryamtahhan/collectd","description":"The system statistics collection daemon - repo for new features to be merged into mainline collectd. Mirror of the canonical repository (updated automatically). Please send Pull Requests here!","fork":true,"url":"https://api.github.com/repos/maryamtahhan/collectd","forks_url":"https://api.github.com/repos/maryamtahhan/collectd/forks","keys_url":"https://api.github.com/repos/maryamtahhan/collectd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/maryamtahhan/collectd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/maryamtahhan/collectd/teams","hooks_url":"https://api.github.com/repos/maryamtahhan/collectd/hooks","issue_events_url":"https://api.github.com/repos/maryamtahhan/collectd/issues/events{/number}","events_url":"https://api.github.com/repos/maryamtahhan/collectd/events","assignees_url":"https://api.github.com/repos/maryamtahhan/collectd/assignees{/user}","branches_url":"https://api.github.com/repos/maryamtahhan/collectd/branches{/branch}","tags_url":"https://api.github.com/repos/maryamtahhan/collectd/tags","blobs_url":"https://api.github.com/repos/maryamtahhan/collectd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/maryamtahhan/collectd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/maryamtahhan/collectd/git/refs{/sha}","trees_url":"https://api.github.com/repos/maryamtahhan/collectd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/maryamtahhan/collectd/statuses/{sha}","languages_url":"https://api.github.com/repos/maryamtahhan/collectd/languages","stargazers_url":"https://api.github.com/repos/maryamtahhan/collectd/stargazers","contributors_url":"https://api.github.com/repos/maryamtahhan/collectd/contributors","subscribers_url":"https://api.github.com/repos/maryamtahhan/collectd/subscribers","subscription_url":"https://api.github.com/repos/maryamtahhan/collectd/subscription","commits_url":"https://api.github.com/repos/maryamtahhan/collectd/commits{/sha}","git_commits_url":"https://api.github.com/repos/maryamtahhan/collectd/git/commits{/sha}","comments_url":"https://api.github.com/repos/maryamtahhan/collectd/comments{/number}","issue_comment_url":"https://api.github.com/repos/maryamtahhan/collectd/issues/comments{/number}","contents_url":"https://api.github.com/repos/maryamtahhan/collectd/contents/{+path}","compare_url":"https://api.github.com/repos/maryamtahhan/collectd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/maryamtahhan/collectd/merges","archive_url":"https://api.github.com/repos/maryamtahhan/collectd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/maryamtahhan/collectd/downloads","issues_url":"https://api.github.com/repos/maryamtahhan/collectd/issues{/number}","pulls_url":"https://api.github.com/repos/maryamtahhan/collectd/pulls{/number}","milestones_url":"https://api.github.com/repos/maryamtahhan/collectd/milestones{/number}","notifications_url":"https://api.github.com/repos/maryamtahhan/collectd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/maryamtahhan/collectd/labels{/name}","releases_url":"https://api.github.com/repos/maryamtahhan/collectd/releases{/id}","deployments_url":"https://api.github.com/repos/maryamtahhan/collectd/deployments","created_at":"2016-04-01T15:03:06Z","updated_at":"2016-08-24T09:19:46Z","pushed_at":"2016-09-12T15:23:46Z","git_url":"git://github.com/maryamtahhan/collectd.git","ssh_url":"git@github.com:maryamtahhan/collectd.git","clone_url":"https://github.com/maryamtahhan/collectd.git","svn_url":"https://github.com/maryamtahhan/collectd","homepage":"http://collectd.org/","size":14703,"stargazers_count":1,"watchers_count":1,"language":"C","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"collectd:master","ref":"master","sha":"42c129f80a181553ac08b6e8d3484a845c674a07","user":{"login":"collectd","id":1027481,"avatar_url":"https://avatars.githubusercontent.com/u/1027481?v=3","gravatar_id":"","url":"https://api.github.com/users/collectd","html_url":"https://github.com/collectd","followers_url":"https://api.github.com/users/collectd/followers","following_url":"https://api.github.com/users/collectd/following{/other_user}","gists_url":"https://api.github.com/users/collectd/gists{/gist_id}","starred_url":"https://api.github.com/users/collectd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/collectd/subscriptions","organizations_url":"https://api.github.com/users/collectd/orgs","repos_url":"https://api.github.com/users/collectd/repos","events_url":"https://api.github.com/users/collectd/events{/privacy}","received_events_url":"https://api.github.com/users/collectd/received_events","type":"Organization","site_admin":false},"repo":{"id":3208640,"name":"collectd","full_name":"collectd/collectd","owner":{"login":"collectd","id":1027481,"avatar_url":"https://avatars.githubusercontent.com/u/1027481?v=3","gravatar_id":"","url":"https://api.github.com/users/collectd","html_url":"https://github.com/collectd","followers_url":"https://api.github.com/users/collectd/followers","following_url":"https://api.github.com/users/collectd/following{/other_user}","gists_url":"https://api.github.com/users/collectd/gists{/gist_id}","starred_url":"https://api.github.com/users/collectd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/collectd/subscriptions","organizations_url":"https://api.github.com/users/collectd/orgs","repos_url":"https://api.github.com/users/collectd/repos","events_url":"https://api.github.com/users/collectd/events{/privacy}","received_events_url":"https://api.github.com/users/collectd/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/collectd/collectd","description":"The system statistics collection daemon. Mirror of the canonical repository (updated automatically). Please send Pull Requests here!","fork":false,"url":"https://api.github.com/repos/collectd/collectd","forks_url":"https://api.github.com/repos/collectd/collectd/forks","keys_url":"https://api.github.com/repos/collectd/collectd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/collectd/collectd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/collectd/collectd/teams","hooks_url":"https://api.github.com/repos/collectd/collectd/hooks","issue_events_url":"https://api.github.com/repos/collectd/collectd/issues/events{/number}","events_url":"https://api.github.com/repos/collectd/collectd/events","assignees_url":"https://api.github.com/repos/collectd/collectd/assignees{/user}","branches_url":"https://api.github.com/repos/collectd/collectd/branches{/branch}","tags_url":"https://api.github.com/repos/collectd/collectd/tags","blobs_url":"https://api.github.com/repos/collectd/collectd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/collectd/collectd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/collectd/collectd/git/refs{/sha}","trees_url":"https://api.github.com/repos/collectd/collectd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/collectd/collectd/statuses/{sha}","languages_url":"https://api.github.com/repos/collectd/collectd/languages","stargazers_url":"https://api.github.com/repos/collectd/collectd/stargazers","contributors_url":"https://api.github.com/repos/collectd/collectd/contributors","subscribers_url":"https://api.github.com/repos/collectd/collectd/subscribers","subscription_url":"https://api.github.com/repos/collectd/collectd/subscription","commits_url":"https://api.github.com/repos/collectd/collectd/commits{/sha}","git_commits_url":"https://api.github.com/repos/collectd/collectd/git/commits{/sha}","comments_url":"https://api.github.com/repos/collectd/collectd/comments{/number}","issue_comment_url":"https://api.github.com/repos/collectd/collectd/issues/comments{/number}","contents_url":"https://api.github.com/repos/collectd/collectd/contents/{+path}","compare_url":"https://api.github.com/repos/collectd/collectd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/collectd/collectd/merges","archive_url":"https://api.github.com/repos/collectd/collectd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/collectd/collectd/downloads","issues_url":"https://api.github.com/repos/collectd/collectd/issues{/number}","pulls_url":"https://api.github.com/repos/collectd/collectd/pulls{/number}","milestones_url":"https://api.github.com/repos/collectd/collectd/milestones{/number}","notifications_url":"https://api.github.com/repos/collectd/collectd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/collectd/collectd/labels{/name}","releases_url":"https://api.github.com/repos/collectd/collectd/releases{/id}","deployments_url":"https://api.github.com/repos/collectd/collectd/deployments","created_at":"2012-01-18T13:07:51Z","updated_at":"2016-09-14T03:13:15Z","pushed_at":"2016-09-14T11:59:16Z","git_url":"git://github.com/collectd/collectd.git","ssh_url":"git@github.com:collectd/collectd.git","clone_url":"https://github.com/collectd/collectd.git","svn_url":"https://github.com/collectd/collectd","homepage":"http://collectd.org/","size":34562,"stargazers_count":1260,"watchers_count":1260,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":711,"mirror_url":null,"open_issues_count":295,"forks":711,"open_issues":295,"watchers":1260,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/collectd/collectd/pulls/1649"},"html":{"href":"https://github.com/collectd/collectd/pull/1649"},"issue":{"href":"https://api.github.com/repos/collectd/collectd/issues/1649"},"comments":{"href":"https://api.github.com/repos/collectd/collectd/issues/1649/comments"},"review_comments":{"href":"https://api.github.com/repos/collectd/collectd/pulls/1649/comments"},"review_comment":{"href":"https://api.github.com/repos/collectd/collectd/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/collectd/collectd/pulls/1649/commits"},"statuses":{"href":"https://api.github.com/repos/collectd/collectd/statuses/acbf4e5d5b848540c6fc0d9bfb235f6441564384"}}}}
{"actor":{"display_login":"octo"}}