blob: 22263469f4f7eb9ce3c0bb81141175ca8ff0fc7c [file] [log] [blame]
#!/usr/bin/python
'''
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''
import sys
import urllib2
import json
import base64
import optparse
HTTP_PROTOCOL = 'http'
HTTPS_PROTOCOL = 'https'
SOLR_SERVICE_NAME = 'AMBARI_INFRA_SOLR'
SOLR_COMPONENT_NAME ='INFRA_SOLR'
CLUSTERS_URL = '/api/v1/clusters/{0}'
GET_HOSTS_COMPONENTS_URL = '/services/{0}/components/{1}?fields=host_components'
REQUESTS_API_URL = '/requests'
def api_accessor(host, username, password, protocol, port):
def do_request(api_url, request_type, request_body=''):
try:
url = '{0}://{1}:{2}{3}'.format(protocol, host, port, api_url)
print 'Execute {0} {1}'.format(request_type, url)
if request_body:
print 'Request body: {0}'.format(request_body)
admin_auth = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
request = urllib2.Request(url)
request.add_header('Authorization', 'Basic %s' % admin_auth)
request.add_header('X-Requested-By', 'ambari')
request.add_data(request_body)
request.get_method = lambda: request_type
response = urllib2.urlopen(request)
response_body = response.read()
except Exception as exc:
raise Exception('Problem with accessing api. Reason: {0}'.format(exc))
return response_body
return do_request
def format_json(dictionary, tab_level=0):
output = ''
tab = ' ' * 2 * tab_level
for key, value in dictionary.iteritems():
output += ',\n{0}"{1}": '.format(tab, key)
if isinstance(value, dict):
output += '{\n' + format_json(value, tab_level + 1) + tab + '}'
else:
output += '"{0}"'.format(value)
output += '\n'
return output[2:]
def read_json(json_file):
with open(json_file) as data_file:
data = json.load(data_file)
return data
def get_json(accessor, url):
response = accessor(url, 'GET')
print 'GET ' + url + ' response: '
print '----------------------------'
print response
json_resp = json.loads(response)
return json_resp
def post_json(accessor, url, request_body):
response = accessor(url, 'POST', json.dumps(request_body))
print 'POST ' + url + ' response: '
print '----------------------------'
print response
json_resp = json.loads(response)
return json_resp
def get_component_hosts(host_components_json):
hosts = []
if "host_components" in host_components_json and len(host_components_json['host_components']) > 0:
for host_component in host_components_json['host_components']:
if 'HostRoles' in host_component:
hosts.append(host_component['HostRoles']['host_name'])
return hosts
def create_command_request(command, parameters, hosts, cluster, context):
request = {}
request_info = {}
request_info["context"] = context
request_info["command"] = command
request_info["parameters"] = parameters
operation_level = {}
operation_level["level"] = "HOST_COMPONENT"
operation_level["cluster_name"] = cluster
request_info["operation_level"] = operation_level
request["RequestInfo"] = request_info
resource_filter = {}
resource_filter["service_name"] = SOLR_SERVICE_NAME
resource_filter["component_name"] = SOLR_COMPONENT_NAME
resource_filter["hosts"] = ','.join(hosts)
resource_filters = []
resource_filters.append(resource_filter)
request["Requests/resource_filters"] = resource_filters
return request
def fill_parameters(options):
params = {}
if options.collection:
params['solr_collection'] = options.collection
if options.index_location:
params['solr_index_location'] = options.index_location
if options.backup_name:
params['solr_backup_name'] = options.backup_name
if options.index_version:
params['solr_index_version'] = options.index_version
if options.force:
params['solr_index_upgrade_force'] = options.force
if options.async:
params['solr_request_async'] = options.async
if options.request_tries:
params['solr_request_tries'] = options.request_tries
if options.request_time_interval:
params['solr_request_time_interval'] = options.request_time_interval
if options.disable_solr_host_check:
params['solr_check_hosts'] = False
if options.core_filter:
params['solr_core_filter'] = options.core_filter
if options.core_filter:
params['solr_skip_cores'] = options.skip_cores
if options.solr_shards:
params['solr_shards'] = options.solr_shards
if options.solr_hdfs_path:
params['solr_hdfs_path'] = options.solr_hdfs_path
if options.solr_keep_backup:
params['solr_keep_backup'] = True
return params
def validte_common_options(options, parser):
if not options.index_location:
parser.print_help()
print 'index-location option is required'
sys.exit(1)
if not options.collection:
parser.print_help()
print 'collection option is required'
sys.exit(1)
def get_solr_hosts(options, accessor):
if options.solr_hosts:
component_hosts = options.solr_hosts.split(",")
else:
host_components_json = get_json(accessor, CLUSTERS_URL.format(options.cluster) + GET_HOSTS_COMPONENTS_URL.format(SOLR_SERVICE_NAME, SOLR_COMPONENT_NAME))
component_hosts = get_component_hosts(host_components_json)
return component_hosts
def restore(options, accessor, parser):
"""
Send restore solr collection custom command request to ambari-server
"""
validte_common_options(options, parser)
if not options.backup_name:
parser.print_help()
print 'backup-name option is required'
sys.exit(1)
component_hosts = get_solr_hosts(options, accessor)
parameters = fill_parameters(options)
cmd_request = create_command_request("RESTORE", parameters, component_hosts, options.cluster, 'Restore Solr Collection: ' + options.collection)
post_json(accessor, CLUSTERS_URL.format(options.cluster) + REQUESTS_API_URL, cmd_request)
def migrate(options, accessor, parser):
"""
Send migrate lucene index custom command request to ambari-server
"""
validte_common_options(options, parser)
component_hosts = get_solr_hosts(options, accessor)
parameters = fill_parameters(options)
cmd_request = create_command_request("MIGRATE", parameters, component_hosts, options.cluster, 'Migrating Solr Collection: ' + options.collection)
post_json(accessor, CLUSTERS_URL.format(options.cluster) + REQUESTS_API_URL, cmd_request)
def backup(options, accessor, parser):
"""
Send backup solr collection custom command request to ambari-server
"""
validte_common_options(options, parser)
if not options.backup_name:
parser.print_help()
print 'backup-name option is required'
sys.exit(1)
component_hosts = get_solr_hosts(options, accessor)
parameters = fill_parameters(options)
cmd_request = create_command_request("BACKUP", parameters, component_hosts, options.cluster, 'Backup Solr Collection: ' + options.collection)
post_json(accessor, CLUSTERS_URL.format(options.cluster) + REQUESTS_API_URL, cmd_request)
if __name__=="__main__":
parser = optparse.OptionParser("usage: %prog [options]")
parser.add_option("-H", "--host", dest="host", default="localhost", type="string", help="hostname for ambari server")
parser.add_option("-P", "--port", dest="port", default=8080, type="int", help="port number for ambari server")
parser.add_option("-c", "--cluster", dest="cluster", type="string", help="name cluster")
parser.add_option("-s", "--ssl", dest="ssl", action="store_true", help="use if ambari server using https")
parser.add_option("-u", "--username", dest="username", default="admin", type="string", help="username for accessing ambari server")
parser.add_option("-p", "--password", dest="password", default="admin", type="string", help="password for accessing ambari server")
parser.add_option("-a", "--action", dest="action", type="string", help="backup | restore | migrate ")
parser.add_option("-f", "--force", dest="force", default=False, action="store_true", help="force index upgrade even if it's the right version")
parser.add_option("--index-location", dest="index_location", type="string", help="location of the index backups")
parser.add_option("--backup-name", dest="backup_name", type="string", help="backup name of the index")
parser.add_option("--collection", dest="collection", type="string", help="solr collection")
parser.add_option("--version", dest="index_version", type="string", default="6.6.2", help="lucene index version for migration (6.6.2 or 7.3.1)")
parser.add_option("--request-tries", dest="request_tries", type="int", help="number of tries for BACKUP/RESTORE status api calls in the request")
parser.add_option("--request-time-interval", dest="request_time_interval", type="int", help="time interval between BACKUP/RESTORE status api calls in the request")
parser.add_option("--request-async", dest="async", action="store_true", default=False, help="skip BACKUP/RESTORE status api calls from the command")
parser.add_option("--shared-fs", dest="shared_fs", action="store_true", default=False, help="shared fs for storing backup (will create index location to <path><hostname>)")
parser.add_option("--solr-hosts", dest="solr_hosts", type="string", help="comma separated list of solr hosts")
parser.add_option("--disable-solr-host-check", dest="disable_solr_host_check", action="store_true", default=False, help="Disable to check solr hosts are good for the collection backups")
parser.add_option("--core-filter", dest="core_filter", default=None, type="string", help="core filter for replica folders")
parser.add_option("--skip-cores", dest="skip_cores", default=None, type="string", help="specific cores to skip (comma separated)")
parser.add_option("--shards", dest="solr_shards", type="int", default=0, help="number of shards (required to set properly for restore)")
parser.add_option("--solr-hdfs-path", dest="solr_hdfs_path", type="string", default=None, help="Base path of Solr (where collections are located) if HDFS is used (like /user/infra-solr)")
parser.add_option("--solr-keep-backup", dest="solr_keep_backup", default=False, action="store_true", help="If it is turned on, Snapshot Solr data will not be deleted from the filesystem during restore.")
(options, args) = parser.parse_args()
if options.action is None:
parser.print_help()
print 'action option is missing'
else:
protocol = 'https' if options.ssl else 'http'
accessor = api_accessor(options.host, options.username, options.password, protocol, options.port)
print 'Inputs: ' + str(options)
if options.action.lower() == 'backup':
backup(options, accessor, parser)
elif options.action.lower() == 'restore':
restore(options, accessor, parser)
elif options.action.lower() == 'migrate':
migrate(options, accessor, parser)
else:
parser.print_help()
print 'action option is invalid (available actions: backup | restore | migrate)'