commit
c84e91bad1
@ -1,10 +1,27 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
|
||||
PasteDeploy==1.5.2
|
||||
keystonemiddleware==4.17.0
|
||||
falcon==1.2.0
|
||||
python-dateutil==2.6.1
|
||||
requests==2.18.4
|
||||
uwsgi===2.0.15
|
||||
uwsgi==2.0.15
|
||||
configparser==3.5.0
|
||||
python-openstackclient==3.11.0
|
||||
SQLAlchemy==1.1.13
|
||||
psycopg2==2.7.3.1
|
||||
PasteDeploy==1.5.2
|
||||
keystonemiddleware===4.9.1
|
||||
oslo.config==4.11.0
|
||||
oslo.policy==1.25.1
|
||||
keystoneauth1==2.13.0
|
||||
|
@ -0,0 +1,202 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
"""Single point of entry to generate the sample configuration file.
|
||||
This module collects all the necessary info from the other modules in this
|
||||
package. It is assumed that:
|
||||
* Every other module in this package has a 'list_opts' function which
|
||||
returns a dict where:
|
||||
* The keys are strings which are the group names.
|
||||
* The value of each key is a list of config options for that group.
|
||||
* The conf package doesn't have further packages with config options.
|
||||
* This module is only used in the context of sample file generation.
|
||||
"""
|
||||
import importlib
|
||||
import os
|
||||
import pkgutil
|
||||
|
||||
from oslo_config import cfg
|
||||
import keystoneauth1.loading as loading
|
||||
|
||||
IGNORED_MODULES = ('shipyard', 'config')
|
||||
|
||||
if (os.path.exists('etc/shipyard/shipyard.conf')):
|
||||
cfg.CONF(['--config-file', 'etc/shipyard/shipyard.conf'])
|
||||
|
||||
class ShipyardConfig(object):
|
||||
"""
|
||||
Initialize all the core options
|
||||
"""
|
||||
# Default options
|
||||
options = [
|
||||
cfg.IntOpt(
|
||||
'poll_interval',
|
||||
default=10,
|
||||
help=[
|
||||
'''Polling interval in seconds for checking subtask or
|
||||
downstream status'''
|
||||
]),
|
||||
]
|
||||
|
||||
# Logging options
|
||||
logging_options = [
|
||||
cfg.StrOpt(
|
||||
'log_level', default='INFO', help='Global log level for Shipyard'),
|
||||
cfg.StrOpt(
|
||||
'global_logger_name',
|
||||
default='shipyard',
|
||||
help='Logger name for the top-level logger'),
|
||||
]
|
||||
|
||||
# Enabled plugins
|
||||
plugin_options = [
|
||||
cfg.MultiStrOpt(
|
||||
'ingester',
|
||||
default=['shipyard_airflow.ingester.plugins.yaml.YamlIngester'],
|
||||
help='Module path string of a input ingester to enable'),
|
||||
cfg.MultiStrOpt(
|
||||
'oob_driver',
|
||||
default=[
|
||||
'shipyard_airflow.drivers.oob.pyghmi_driver.PyghmiDriver'
|
||||
],
|
||||
help='Module path string of a OOB driver to enable'),
|
||||
cfg.StrOpt(
|
||||
'node_driver',
|
||||
default=[
|
||||
'''shipyard_airflow.drivers.node.maasdriver.driver
|
||||
.MaasNodeDriver'''
|
||||
],
|
||||
help='Module path string of the Node driver to enable'),
|
||||
# TODO Network driver not yet implemented
|
||||
cfg.StrOpt(
|
||||
'network_driver',
|
||||
default=None,
|
||||
help='Module path string of the Network driver enable'),
|
||||
]
|
||||
|
||||
# Timeouts for various tasks specified in minutes
|
||||
timeout_options = [
|
||||
cfg.IntOpt(
|
||||
'shipyard_timeout',
|
||||
default=5,
|
||||
help='Fallback timeout when a specific one is not configured'),
|
||||
cfg.IntOpt(
|
||||
'create_network_template',
|
||||
default=2,
|
||||
help='Timeout in minutes for creating site network templates'),
|
||||
cfg.IntOpt(
|
||||
'configure_user_credentials',
|
||||
default=2,
|
||||
help='Timeout in minutes for creating user credentials'),
|
||||
cfg.IntOpt(
|
||||
'identify_node',
|
||||
default=10,
|
||||
help='Timeout in minutes for initial node identification'),
|
||||
cfg.IntOpt(
|
||||
'configure_hardware',
|
||||
default=30,
|
||||
help=[
|
||||
'''Timeout in minutes for node commissioning and
|
||||
hardware configuration'''
|
||||
]),
|
||||
cfg.IntOpt(
|
||||
'apply_node_networking',
|
||||
default=5,
|
||||
help='Timeout in minutes for configuring node networking'),
|
||||
cfg.IntOpt(
|
||||
'apply_node_platform',
|
||||
default=5,
|
||||
help='Timeout in minutes for configuring node platform'),
|
||||
cfg.IntOpt(
|
||||
'deploy_node',
|
||||
default=45,
|
||||
help='Timeout in minutes for deploying a node'),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.conf = cfg.CONF
|
||||
|
||||
def register_options(self):
|
||||
self.conf.register_opts(ShipyardConfig.options)
|
||||
self.conf.register_opts(
|
||||
ShipyardConfig.logging_options, group='logging')
|
||||
self.conf.register_opts(ShipyardConfig.plugin_options, group='plugins')
|
||||
self.conf.register_opts(
|
||||
ShipyardConfig.timeout_options, group='timeouts')
|
||||
self.conf.register_opts(
|
||||
loading.get_auth_plugin_conf_options('password'),
|
||||
group='keystone_authtoken')
|
||||
|
||||
|
||||
config_mgr = ShipyardConfig()
|
||||
|
||||
|
||||
def list_opts():
|
||||
opts = {
|
||||
'DEFAULT': ShipyardConfig.options,
|
||||
'logging': ShipyardConfig.logging_options,
|
||||
'plugins': ShipyardConfig.plugin_options,
|
||||
'timeouts': ShipyardConfig.timeout_options
|
||||
}
|
||||
|
||||
package_path = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_module = ".".join(__name__.split('.')[:-1])
|
||||
module_names = _list_module_names(package_path, parent_module)
|
||||
imported_modules = _import_modules(module_names)
|
||||
_append_config_options(imported_modules, opts)
|
||||
# Assume we'll use the password plugin,
|
||||
# so include those options in the configuration template
|
||||
opts['keystone_authtoken'] = loading.get_auth_plugin_conf_options(
|
||||
'password')
|
||||
return _tupleize(opts)
|
||||
|
||||
|
||||
def _tupleize(d):
|
||||
"""Convert a dict of options to the 2-tuple format."""
|
||||
return [(key, value) for key, value in d.items()]
|
||||
|
||||
|
||||
def _list_module_names(pkg_path, parent_module):
|
||||
module_names = []
|
||||
for _, module_name, ispkg in pkgutil.iter_modules(path=[pkg_path]):
|
||||
if module_name in IGNORED_MODULES:
|
||||
# Skip this module.
|
||||
continue
|
||||
elif ispkg:
|
||||
module_names.extend(
|
||||
_list_module_names(pkg_path + "/" + module_name,
|
||||
parent_module + "." + module_name))
|
||||
else:
|
||||
module_names.append(parent_module + "." + module_name)
|
||||
return module_names
|
||||
|
||||
|
||||
def _import_modules(module_names):
|
||||
imported_modules = []
|
||||
for module_name in module_names:
|
||||
module = importlib.import_module(module_name)
|
||||
if hasattr(module, 'list_opts'):
|
||||
print("Pulling options from module %s" % module.__name__)
|
||||
imported_modules.append(module)
|
||||
return imported_modules
|
||||
|
||||
|
||||
def _append_config_options(imported_modules, config_options):
|
||||
for module in imported_modules:
|
||||
configs = module.list_opts()
|
||||
for key, val in configs.items():
|
||||
if key not in config_options:
|
||||
config_options[key] = val
|
||||
else:
|
||||
config_options[key].extend(val)
|
@ -1,78 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
from urllib.parse import urlunsplit
|
||||
from falcon import HTTPInvalidParam
|
||||
|
||||
from .base import BaseResource
|
||||
from shipyard_airflow.airflow_client import AirflowClient
|
||||
|
||||
# We need to be able to add/delete connections so that we can create/delete
|
||||
# connection endpoints that Airflow needs to connect to
|
||||
class AirflowAddConnectionResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, action, conn_id, protocol, host, port):
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
if action != 'add':
|
||||
raise HTTPInvalidParam(
|
||||
'Invalid Paremeters for Adding Airflow Connection', 'action')
|
||||
|
||||
# Concatenate to form the connection URL
|
||||
netloc = ''.join([host, ':', port])
|
||||
url = (protocol, netloc, '', '', '')
|
||||
conn_uri = urlunsplit(url)
|
||||
# Form the request URL towards Airflow
|
||||
req_url = ('{}/admin/rest_api/api?api=connections&add=true&conn_id'
|
||||
'={}&conn_uri={}'.format(web_server_url, conn_id, conn_uri))
|
||||
|
||||
airflow_client = AirflowClient(req_url)
|
||||
self.on_success(resp, airflow_client.get())
|
||||
|
||||
|
||||
# Delete a particular connection endpoint
|
||||
class AirflowDeleteConnectionResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, action, conn_id):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
if action != 'delete':
|
||||
raise HTTPInvalidParam(
|
||||
'Invalid Paremeters for Deleting Airflow Connection', 'action')
|
||||
|
||||
# Form the request URL towards Airflow
|
||||
req_url = ('{}/admin/rest_api/api?api=connections&delete=true&conn_id'
|
||||
'={}'.format(web_server_url, conn_id))
|
||||
airflow_client = AirflowClient(req_url)
|
||||
self.on_success(resp, airflow_client.get())
|
||||
|
||||
|
||||
# List all current connection endpoints
|
||||
class AirflowListConnectionsResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, action):
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
if action != 'list':
|
||||
raise HTTPInvalidParam(
|
||||
'Invalid Paremeters for listing Airflow Connections', 'action')
|
||||
|
||||
req_url = '{}/admin/rest_api/api?api=connections&list=true'.format(
|
||||
web_server_url)
|
||||
|
||||
airflow_client = AirflowClient(req_url)
|
||||
self.on_success(resp, airflow_client.get())
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class GetDagStateResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, dag_id, execution_date):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
req_url = ('{}/admin/rest_api/api?api=dag_state&dag_id={}'
|
||||
'&execution_date={}'.format(web_server_url, dag_id,
|
||||
execution_date))
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["output"]["stderr"]:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]["stderr"]
|
||||
return
|
||||
else:
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = response["output"]["stdout"]
|
@ -1,46 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class GetTaskStatusResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, dag_id, task_id, execution_date):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
|
||||
req_url = (
|
||||
'{}/admin/rest_api/api?api=task_state&dag_id={}&task_id={}'
|
||||
'&execution_date={}'.format(web_server_url, dag_id, task_id,
|
||||
execution_date))
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["output"]["stderr"]:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]["stderr"]
|
||||
return
|
||||
else:
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = response["output"]["stdout"]
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class GetAirflowVersionResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
# Get Airflow Version
|
||||
req_url = '{}/admin/rest_api/api?api=version'.format(
|
||||
web_server_url)
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["output"]:
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = response["output"]
|
||||
else:
|
||||
self.return_error(resp, falcon.HTTP_400,
|
||||
'Fail to Retrieve Airflow Version')
|
||||
return
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class ListDagsResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
# List available dags
|
||||
req_url = '{}/admin/rest_api/api?api=list_dags'.format(
|
||||
web_server_url)
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["output"]["stderr"]:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]["stderr"]
|
||||
return
|
||||
else:
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = response["output"]["stdout"]
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class ListTasksResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, dag_id):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
# Retrieve all tasks belonging to a particular Dag
|
||||
req_url = '{}/admin/rest_api/api?api=list_tasks&dag_id={}'.format(
|
||||
web_server_url, dag_id)
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["output"]["stderr"]:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]["stderr"]
|
||||
return
|
||||
else:
|
||||
resp.status = falcon.HTTP_200
|
||||
resp.body = response["output"]["stdout"]
|
@ -1,54 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from dateutil.parser import parse
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class TriggerDagRunResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, dag_id, conf):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
# "conf" - JSON string that gets pickled into the DagRun's
|
||||
# conf attribute
|
||||
req_url = ('{}/admin/rest_api/api?api=trigger_dag&dag_id={}'
|
||||
'&conf={}'.format(web_server_url, dag_id, conf))
|
||||
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
# Returns error response if API call returns
|
||||
# response code other than 200
|
||||
if response["http_response_code"] != 200:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]
|
||||
return
|
||||
else:
|
||||
# Returns 201 if action is created successfully
|
||||
resp.status = falcon.HTTP_201
|
||||
|
||||
# Return time of execution so that we can use
|
||||
# it to query dag/task status
|
||||
dt = parse(response["response_time"])
|
||||
resp.body = dt.strftime('%Y-%m-%dT%H:%M:%S')
|
@ -1,92 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
import logging
|
||||
|
||||
from dateutil.parser import parse
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class TriggerDagRunPollResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_get(self, req, resp, dag_id, run_id):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
req_url = ('{}/admin/rest_api/api?api=trigger_dag&dag_id={}'
|
||||
'&run_id={}'.format(web_server_url, dag_id, run_id))
|
||||
response = requests.get(req_url).json()
|
||||
|
||||
if response["http_response_code"] != 200:
|
||||
resp.status = falcon.HTTP_400
|
||||
resp.body = response["output"]
|
||||
return
|
||||
else:
|
||||
resp.status = falcon.HTTP_200
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logging.info("Executing '" + dag_id + "' Dag...")
|
||||
|
||||
# Retrieve time of execution so that we
|
||||
# can use it to query dag/task status
|
||||
dt = parse(response["response_time"])
|
||||
exec_date = dt.strftime('%Y-%m-%dT%H:%M:%S')
|
||||
|
||||
url = ('{}/admin/rest_api/api'
|
||||
'?api=dag_state&dag_id={}&execution_date={}'.format(
|
||||
web_server_url, dag_id, exec_date))
|
||||
|
||||
# Back off for 5 seconds before querying the initial state
|
||||
time.sleep(5)
|
||||
dag_state = requests.get(url).json()
|
||||
|
||||
# Remove newline character at the end of the response
|
||||
dag_state = dag_state["output"]["stdout"].encode(
|
||||
'utf8').rstrip()
|
||||
|
||||
while dag_state != 'success':
|
||||
# Get current state
|
||||
dag_state = requests.get(url).json()
|
||||
|
||||
# Remove newline character at the end of the response
|
||||
dag_state = dag_state["output"]["stdout"].encode(
|
||||
'utf8').rstrip()
|
||||
|
||||
# Logs output of current dag state
|
||||
logging.info('Current Dag State: ' + dag_state)
|
||||
|
||||
if dag_state == 'failed':
|
||||
resp.status = falcon.HTTP_500
|
||||
logging.info('Dag Execution Failed')
|
||||
resp.body = json.dumps({
|
||||
'Error': 'Dag Execution Failed'
|
||||
})
|
||||
return
|
||||
|
||||
# Wait for 20 seconds before doing a new query
|
||||
time.sleep(20)
|
||||
|
||||
logging.info('Dag Successfully Executed')
|
@ -0,0 +1,25 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
|
||||
#PasteDeploy Configuration File
|
||||
#Used to configure uWSGI middleware pipeline
|
||||
|
||||
[app:shipyard-api]
|
||||
paste.app_factory = shipyard_airflow.shipyard:paste_start_shipyard
|
||||
|
||||
[pipeline:main]
|
||||
pipeline = authtoken shipyard-api
|
||||
|
||||
[filter:authtoken]
|
||||
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
|
@ -1,54 +0,0 @@
|
||||
# Copyright 2017 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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 falcon
|
||||
import requests
|
||||
|
||||
from .base import BaseResource
|
||||
|
||||
|
||||
class DagRunResource(BaseResource):
|
||||
|
||||
authorized_roles = ['user']
|
||||
|
||||
def on_post(self,
|
||||
req,
|
||||
resp,
|
||||
dag_id,
|
||||
run_id=None,
|
||||
conf=None,
|
||||
execution_date=None):
|
||||
# Retrieve URL
|
||||
web_server_url = self.retrieve_config('base', 'web_server')
|
||||
|
||||
if 'Error' in web_server_url:
|
||||
resp.status = falcon.HTTP_500
|
||||
raise falcon.HTTPInternalServerError("Internal Server Error",
|
||||
"Missing Configuration File")
|
||||
else:
|
||||
req_url = '{}/api/experimental/dags/{}/dag_runs'.format(
|
||||
web_server_url, dag_id)
|
||||
|
||||
response = requests.post(
|
||||
req_url,
|
||||
json={
|
||||
"run_id": run_id,
|
||||
"conf": conf,
|
||||
"execution_date": execution_date,
|
||||
})
|
||||
|
||||
if response.ok:
|
||||
resp.status = falcon.HTTP_200
|
||||
else:
|
||||
self.return_error(resp, falcon.HTTP_400, 'Fail to Execute Dag')
|
||||
return
|
@ -1,36 +0,0 @@
|
||||
[base]
|
||||
web_server=http://localhost:32080
|
||||
postgresql_db = postgresql+psycopg2://postgresql.ucp:5432/shipyard
|
||||
postgresql_airflow_db = postgresql+psycopg2://postgresql.ucp:5432/airflow
|
||||
|
||||
[shipyard]
|
||||
host=shipyard-int.ucp
|
||||
port=9000
|
||||
|
||||
[deckhand]
|
||||
host=deckhand-api.ucp
|
||||
port=80
|
||||
|
||||
[armada]
|
||||
host=armada-api.ucp
|
||||
port=8000
|
||||
|
||||
[drydock]
|
||||
host=drydock-api.ucp
|
||||
port=9000
|
||||
token=bigboss
|
||||
site_yaml=/usr/local/airflow/plugins/drydock.yaml
|
||||
prom_yaml=/usr/local/airflow/plugins/promenade.yaml
|
||||
|
||||
[keystone]
|
||||
OS_AUTH_URL=http://keystone-api.ucp:80/v3
|
||||
OS_PROJECT_NAME=service
|
||||
OS_USER_DOMAIN_NAME=Default
|
||||
OS_USERNAME=shipyard
|
||||
OS_PASSWORD=password
|
||||
OS_REGION_NAME=RegionOne
|
||||
OS_IDENTITY_API_VERSION=3
|
||||
|
||||
[healthcheck]
|
||||
schema=http
|
||||
endpoint=/api/v1.0/health
|