{"action":"created","comment":{"url":"https://api.github.com/repos/lsst/qserv/pulls/comments/27270263","id":27270263,"diff_hunk":"@@ -0,0 +1,692 @@\n+#\n+# LSST Data Management System\n+# Copyright 2015 AURA/LSST.\n+#\n+# This product includes software developed by the\n+# LSST Project (http://www.lsst.org/).\n+#\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 3 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.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the LSST License Statement and\n+# the GNU General Public License along with this program.  If not,\n+# see <http://www.lsstcorp.org/LegalNotices/>.\n+#\n+\n+\"\"\"\n+Module defining Flask blueprint for database management.\n+\n+@author Andy Salnikov - salnikov@slac.stanford.edu\n+\"\"\"\n+\n+#--------------------------------\n+#  Imports of standard modules --\n+#--------------------------------\n+import re\n+import logging\n+\n+#-----------------------------\n+# Imports for other modules --\n+#-----------------------------\n+from .config import Config\n+from .errors import errorResponse, ExceptionResponse\n+from lsst.db import db\n+from lsst.qserv.admin.qservAdminException import QservAdminException\n+from flask import Blueprint, json, request, url_for\n+from MySQLdb.constants import FIELD_TYPE\n+\n+#----------------------------------\n+# Local non-exported definitions --\n+#----------------------------------\n+\n+_log = logging.getLogger('dbMgr')\n+\n+# pattern for valid db/table/column names\n+_idNameRe = re.compile(r'^[a-zA-Z_][0-9a-zA-Z_]*$')\n+\n+def _validateId(idType, identifier):\n+    \"\"\"\n+    Validate identifier, throws exception if identifier contains illegal characters\n+    \"\"\"\n+    if not _idNameRe.match(identifier):\n+        raise ExceptionResponse(400, \"InvalidArgument\",\n+                                \"{0} name is invalid: '{1}'\".format(idType, identifier))\n+\n+def _validateDbName(dbName):\n+    \"\"\" Validate database name \"\"\"\n+    return _validateId(\"Database\", dbName)\n+\n+def _validateTableName(tblName):\n+    \"\"\" Validate table name \"\"\"\n+    return _validateId(\"Table\", tblName)\n+\n+def _validateColumnName(columnName):\n+    \"\"\" Validate column name \"\"\"\n+    return _validateId(\"Column\", columnName)\n+\n+# list of database names that we want to ignore\n+_specialDbs = set(['mysql', 'information_schema'])\n+\n+# pattern for CREATE TABLE\n+_createTableRe = re.compile(r'^\\s*CREATE\\s+TABLE\\s+(\\w\\.)?(\\w+)', re.I)\n+\n+def _dbDict(dbName):\n+    \"\"\" Make database instance dict out of db name \"\"\"\n+    return dict(name=dbName, uri=url_for('.dbDelete', dbName=dbName))\n+\n+def _tblDict(dbName, tblName):\n+    \"\"\" Make table instance dict out of table name \"\"\"\n+    return dict(name=tblName, uri=url_for('.tableDelete', dbName=dbName, tblName=tblName))\n+\n+def _chunkDict(dbName, tblName, chunkId):\n+    \"\"\" Make table instance dict out of table name \"\"\"\n+    uri = url_for('.chunkDelete', dbName=dbName, tblName=tblName, chunkId=chunkId)\n+    return dict(chunkId=chunkId, uri=uri, chunkTable=False, overlapTable=False)\n+\n+def _getArgFlag(mdict, option, default=True):\n+    \"\"\"\n+    Extracts specified argument from request arguments and converts it to boolean flag.\n+    Argument is required ot have value, possible values are '0', '1', 'yes', 'no', 'true', 'false'.\n+    \"\"\"\n+    value = mdict.get(option, None)\n+    if value is None:\n+        return default\n+    value = value.strip().lower()\n+    if value not in ('0', '1', 'yes', 'no', 'true', 'false'):\n+        raise ExceptionResponse(400, \"InvalidArgument\",\n+                                \"Unexpected value of '%s' option: \\\"%s\\\"\" % (option, value))\n+    return value in ('1', 'yes', 'true')\n+\n+def _typeCode2Name(code):\n+    \"\"\"\n+    Convert mysql type code to type name, returns None if ther is no mapping.\n+    This is mysql-specific, it would be better to have technology-neutral way\n+    to provide the same mapping.\n+    \"\"\"\n+    for name in dir(FIELD_TYPE):\n+        if getattr(FIELD_TYPE, name) == code:\n+            return name\n+    return None\n+\n+#------------------------\n+# Exported definitions --\n+#------------------------\n+\n+dbService = Blueprint('dbService', __name__, template_folder='dbService')\n+\n+\n+@dbService.errorhandler(db.DbException)\n+def dbExceptionHandler(error):\n+    \"\"\" All leaked DbException exceptions make 500 error \"\"\"\n+    return errorResponse(500, \"db.DbException\", str(error))\n+\n+@dbService.route('', methods=['GET'])\n+def dbs():\n+    \"\"\" Return the list of databases \"\"\"\n+\n+    _log.debug('request: %s', request)\n+    _log.debug('dbs() GET => get database list')\n+\n+    # use non-privileged account, may limit the list of databases returned.\n+    dbConn = Config.instance().dbConn()\n+    dbs = dbConn.execCommandN('SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA')\n+\n+    dbs = [row[0] for row in dbs]\n+    _log.debug('dbs(): dbs = %s', dbs)\n+\n+    dbData = []\n+    for dbName in dbs:\n+        if dbName not in _specialDbs:\n+            dbData += [_dbDict(dbName)]\n+\n+    return json.jsonify(results=dbData)\n+\n+\n+@dbService.route('', methods=['POST'])\n+def dbCreate():\n+    \"\"\"\n+    Create new database, database name comes in a query string\n+\n+    Following parameters are expected to come in a request (in request body\n+    with application/x-www-form-urlencoded content like regular form):\n+        db: database name (required)\n+    \"\"\"\n+\n+    _log.debug('request: %s', request)\n+    _log.debug('request.form: %s', request.form)\n+    _log.debug('dbCreate() POST => make database')\n+\n+    # get database name from query\n+    dbName = request.form.get('db', '').strip()\n+    if not dbName:\n+        raise ExceptionResponse(400, \"MissingArgument\", \"Database name argument (db) is missing\")\n+\n+    # validate it\n+    _validateDbName(dbName)\n+\n+    # create database, use privileged account\n+    dbConn = Config.instance().privDbConn()\n+    try:\n+        dbConn.createDb(dbName)","path":"core/modules/wmgr/python/dbMgr.py","position":177,"original_position":177,"commit_id":"99cf024bc9e189c76d1538e9094993386772fb69","original_commit_id":"99cf024bc9e189c76d1538e9094993386772fb69","user":{"login":"ktlim","id":1054393,"avatar_url":"https://avatars.githubusercontent.com/u/1054393?v=3","gravatar_id":"","url":"https://api.github.com/users/ktlim","html_url":"https://github.com/ktlim","followers_url":"https://api.github.com/users/ktlim/followers","following_url":"https://api.github.com/users/ktlim/following{/other_user}","gists_url":"https://api.github.com/users/ktlim/gists{/gist_id}","starred_url":"https://api.github.com/users/ktlim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ktlim/subscriptions","organizations_url":"https://api.github.com/users/ktlim/orgs","repos_url":"https://api.github.com/users/ktlim/repos","events_url":"https://api.github.com/users/ktlim/events{/privacy}","received_events_url":"https://api.github.com/users/ktlim/received_events","type":"User","site_admin":false},"body":"I thought that our plan was to handle end-user authentication and authorization at the czar level, not the worker level.  All communication with the worker MySQL would be through a Unix or localhost socket and so would not generally require any sophisticated authentication.  While the inter-node networks are not isolated, the on-node connection would be.","created_at":"2015-03-27T01:04:33Z","updated_at":"2015-03-27T01:04:33Z","html_url":"https://github.com/lsst/qserv/pull/52#discussion_r27270263","pull_request_url":"https://api.github.com/repos/lsst/qserv/pulls/52","_links":{"self":{"href":"https://api.github.com/repos/lsst/qserv/pulls/comments/27270263"},"html":{"href":"https://github.com/lsst/qserv/pull/52#discussion_r27270263"},"pull_request":{"href":"https://api.github.com/repos/lsst/qserv/pulls/52"}}},"pull_request":{"url":"https://api.github.com/repos/lsst/qserv/pulls/52","id":32058782,"html_url":"https://github.com/lsst/qserv/pull/52","diff_url":"https://github.com/lsst/qserv/pull/52.diff","patch_url":"https://github.com/lsst/qserv/pull/52.patch","issue_url":"https://api.github.com/repos/lsst/qserv/issues/52","number":52,"state":"open","locked":false,"title":"u/salnikov/dm-2176","user":{"login":"andy-slac","id":4266950,"avatar_url":"https://avatars.githubusercontent.com/u/4266950?v=3","gravatar_id":"","url":"https://api.github.com/users/andy-slac","html_url":"https://github.com/andy-slac","followers_url":"https://api.github.com/users/andy-slac/followers","following_url":"https://api.github.com/users/andy-slac/following{/other_user}","gists_url":"https://api.github.com/users/andy-slac/gists{/gist_id}","starred_url":"https://api.github.com/users/andy-slac/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andy-slac/subscriptions","organizations_url":"https://api.github.com/users/andy-slac/orgs","repos_url":"https://api.github.com/users/andy-slac/repos","events_url":"https://api.github.com/users/andy-slac/events{/privacy}","received_events_url":"https://api.github.com/users/andy-slac/received_events","type":"User","site_admin":false},"body":"PR for reviewing and merging DM-2176 branch","created_at":"2015-03-26T20:46:30Z","updated_at":"2015-03-27T01:04:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9c3c67a510c3970e529a3a48f13c46b8bdc0a9ed","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/lsst/qserv/pulls/52/commits","review_comments_url":"https://api.github.com/repos/lsst/qserv/pulls/52/comments","review_comment_url":"https://api.github.com/repos/lsst/qserv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lsst/qserv/issues/52/comments","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/99cf024bc9e189c76d1538e9094993386772fb69","head":{"label":"lsst:u/salnikov/DM-2176","ref":"u/salnikov/DM-2176","sha":"99cf024bc9e189c76d1538e9094993386772fb69","user":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"repo":{"id":23013460,"name":"qserv","full_name":"lsst/qserv","owner":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lsst/qserv","description":"LSST Query Services","fork":false,"url":"https://api.github.com/repos/lsst/qserv","forks_url":"https://api.github.com/repos/lsst/qserv/forks","keys_url":"https://api.github.com/repos/lsst/qserv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lsst/qserv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lsst/qserv/teams","hooks_url":"https://api.github.com/repos/lsst/qserv/hooks","issue_events_url":"https://api.github.com/repos/lsst/qserv/issues/events{/number}","events_url":"https://api.github.com/repos/lsst/qserv/events","assignees_url":"https://api.github.com/repos/lsst/qserv/assignees{/user}","branches_url":"https://api.github.com/repos/lsst/qserv/branches{/branch}","tags_url":"https://api.github.com/repos/lsst/qserv/tags","blobs_url":"https://api.github.com/repos/lsst/qserv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lsst/qserv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lsst/qserv/git/refs{/sha}","trees_url":"https://api.github.com/repos/lsst/qserv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/{sha}","languages_url":"https://api.github.com/repos/lsst/qserv/languages","stargazers_url":"https://api.github.com/repos/lsst/qserv/stargazers","contributors_url":"https://api.github.com/repos/lsst/qserv/contributors","subscribers_url":"https://api.github.com/repos/lsst/qserv/subscribers","subscription_url":"https://api.github.com/repos/lsst/qserv/subscription","commits_url":"https://api.github.com/repos/lsst/qserv/commits{/sha}","git_commits_url":"https://api.github.com/repos/lsst/qserv/git/commits{/sha}","comments_url":"https://api.github.com/repos/lsst/qserv/comments{/number}","issue_comment_url":"https://api.github.com/repos/lsst/qserv/issues/comments{/number}","contents_url":"https://api.github.com/repos/lsst/qserv/contents/{+path}","compare_url":"https://api.github.com/repos/lsst/qserv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lsst/qserv/merges","archive_url":"https://api.github.com/repos/lsst/qserv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lsst/qserv/downloads","issues_url":"https://api.github.com/repos/lsst/qserv/issues{/number}","pulls_url":"https://api.github.com/repos/lsst/qserv/pulls{/number}","milestones_url":"https://api.github.com/repos/lsst/qserv/milestones{/number}","notifications_url":"https://api.github.com/repos/lsst/qserv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lsst/qserv/labels{/name}","releases_url":"https://api.github.com/repos/lsst/qserv/releases{/id}","created_at":"2014-08-16T07:50:36Z","updated_at":"2015-03-24T21:16:08Z","pushed_at":"2015-03-27T00:51:12Z","git_url":"git://github.com/lsst/qserv.git","ssh_url":"git@github.com:lsst/qserv.git","clone_url":"https://github.com/lsst/qserv.git","svn_url":"https://github.com/lsst/qserv","homepage":"http://dm.lsst.org/","size":19257,"stargazers_count":4,"watchers_count":4,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":7,"forks":5,"open_issues":7,"watchers":4,"default_branch":"master"}},"base":{"label":"lsst:master","ref":"master","sha":"a6b6a6e64807b2b2885b004ae80567c6c351aae3","user":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"repo":{"id":23013460,"name":"qserv","full_name":"lsst/qserv","owner":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lsst/qserv","description":"LSST Query Services","fork":false,"url":"https://api.github.com/repos/lsst/qserv","forks_url":"https://api.github.com/repos/lsst/qserv/forks","keys_url":"https://api.github.com/repos/lsst/qserv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lsst/qserv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lsst/qserv/teams","hooks_url":"https://api.github.com/repos/lsst/qserv/hooks","issue_events_url":"https://api.github.com/repos/lsst/qserv/issues/events{/number}","events_url":"https://api.github.com/repos/lsst/qserv/events","assignees_url":"https://api.github.com/repos/lsst/qserv/assignees{/user}","branches_url":"https://api.github.com/repos/lsst/qserv/branches{/branch}","tags_url":"https://api.github.com/repos/lsst/qserv/tags","blobs_url":"https://api.github.com/repos/lsst/qserv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lsst/qserv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lsst/qserv/git/refs{/sha}","trees_url":"https://api.github.com/repos/lsst/qserv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/{sha}","languages_url":"https://api.github.com/repos/lsst/qserv/languages","stargazers_url":"https://api.github.com/repos/lsst/qserv/stargazers","contributors_url":"https://api.github.com/repos/lsst/qserv/contributors","subscribers_url":"https://api.github.com/repos/lsst/qserv/subscribers","subscription_url":"https://api.github.com/repos/lsst/qserv/subscription","commits_url":"https://api.github.com/repos/lsst/qserv/commits{/sha}","git_commits_url":"https://api.github.com/repos/lsst/qserv/git/commits{/sha}","comments_url":"https://api.github.com/repos/lsst/qserv/comments{/number}","issue_comment_url":"https://api.github.com/repos/lsst/qserv/issues/comments{/number}","contents_url":"https://api.github.com/repos/lsst/qserv/contents/{+path}","compare_url":"https://api.github.com/repos/lsst/qserv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lsst/qserv/merges","archive_url":"https://api.github.com/repos/lsst/qserv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lsst/qserv/downloads","issues_url":"https://api.github.com/repos/lsst/qserv/issues{/number}","pulls_url":"https://api.github.com/repos/lsst/qserv/pulls{/number}","milestones_url":"https://api.github.com/repos/lsst/qserv/milestones{/number}","notifications_url":"https://api.github.com/repos/lsst/qserv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lsst/qserv/labels{/name}","releases_url":"https://api.github.com/repos/lsst/qserv/releases{/id}","created_at":"2014-08-16T07:50:36Z","updated_at":"2015-03-24T21:16:08Z","pushed_at":"2015-03-27T00:51:12Z","git_url":"git://github.com/lsst/qserv.git","ssh_url":"git@github.com:lsst/qserv.git","clone_url":"https://github.com/lsst/qserv.git","svn_url":"https://github.com/lsst/qserv","homepage":"http://dm.lsst.org/","size":19257,"stargazers_count":4,"watchers_count":4,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":7,"forks":5,"open_issues":7,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lsst/qserv/pulls/52"},"html":{"href":"https://github.com/lsst/qserv/pull/52"},"issue":{"href":"https://api.github.com/repos/lsst/qserv/issues/52"},"comments":{"href":"https://api.github.com/repos/lsst/qserv/issues/52/comments"},"review_comments":{"href":"https://api.github.com/repos/lsst/qserv/pulls/52/comments"},"review_comment":{"href":"https://api.github.com/repos/lsst/qserv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lsst/qserv/pulls/52/commits"},"statuses":{"href":"https://api.github.com/repos/lsst/qserv/statuses/99cf024bc9e189c76d1538e9094993386772fb69"}}}}