#
# This file is part of NoCloud.Net.
#
# Copyright (C) 2022 Last Bastion Network Pty Ltd
#
# NoCloud.Net is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later version.
#
# NoCloud.Net is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# NoCloud.Net. If not, see <https://www.gnu.org/licenses/>.
#
from datetime import datetime
from flask import current_app, request
from noclouddotnet.utils import yaml_response, read_file
from noclouddotnet.models import Instance
from noclouddotnet import db, metrics
from stevedore import extension
from . import instance_blueprint
from dynaconf import settings, Validator
extidmgr = extension.ExtensionManager(
namespace="noclouddotnet.instanceid",
propagate_map_exceptions=True,
invoke_on_load=False,
)
# Register validators
settings.validators.register(
# Ensure some parameters exists (are required)
Validator('INSTANCEID', is_in=extidmgr.entry_points_names()),
)
[docs]@instance_blueprint.route('/user-data', methods=['GET'])
def user_data():
"""
User data (scripts).
:returns: gzip/blob of cloud-int formatted user data
"""
return read_file(current_app.config.USER_DATA)
[docs]@instance_blueprint.route('/vendor-data', methods=['GET'])
def vendor_data():
"""
Vendor data (scripts).
:returns: gzip/blob of cloud-int formatted user data
"""
return read_file(current_app.config.VENDOR_DATA)
[docs]@instance_blueprint.route('/phone-home', methods=['GET', 'POST'])
def phone_home():
"""
A cloud-init phone-home data/save.
The phone-home url should be /phone-home?instance_id=$INSTANCE_ID
Note that a phone-home call only happens once per cloud-instance.
:returns: http return code
"""
form = dict(list(request.form.items()) + list(request.args.items()))
if not form.get('instance_id', None):
current_app.logger.error('phone-home: no instance_id from {}'.format(request.remote_addr))
return yaml_response({'message': 'no instance_id'}, 400)
instance = Instance.query.filter_by(id=form['instance_id']).first()
now = datetime.now()
# hmmm - this shouldn't happen ...
if instance is None:
iid, hostname = extidmgr[current_app.config.INSTANCEID].plugin(request=request)
instance = Instance(id=iid,
remote_ip=request.remote_addr,
first_contact = now,
type = current_app.config.INSTANCE_TYPE,
count = 0)
data = {
'hostname': form.get('hostname',''),
'fqdn': form.get('fqdn',''),
'remote_ip': request.remote_addr,
'last_contact': now,
'pub_key_dsa': form.get('pub_key_dsa',''),
'pub_key_rsa': form.get('pub_key_rsa',''),
'pub_key_ecdsa': form.get('pub_key_ecdsa',''),
'pub_key_ed25519': form.get('pub_key_ed25519',''),
'count': instance.count + 1
}
for k,v in data.items():
setattr(instance, k, v)
db.session.commit()
return yaml_response('')
[docs]@instance_blueprint.route('/fetch', methods=['GET', 'POST'])
def fetch():
"""
Return all registered instance records.
:returns: yaml instance data responding to query
"""
form = dict(list(request.form.items()) + list(request.args.items()))
results = []
if form.get('instance_id', None):
for instance in Instance.query.filter_by(id=form.getlist('instance_id')):
results.append(instance.to_dict())
if form.get('remote_ip', None):
for instance in Instance.query.filter_by(remote_ip=form.getlist('remote_ip')):
results.append(instance.to_dict())
if not form.get('instance_id', None) and not form.get('remote_ip', None):
for instance in Instance.query.all():
results.append(instance.to_dict())
return yaml_response(results)
[docs]@instance_blueprint.route('/debug', methods=['GET'])
@metrics.do_not_track()
def debug():
"""
Show debug info; from request.
:returns: yaml of request and application configuration
"""
results = [
str(request.environ.items()),
current_app.config.items()
]
return yaml_response(results)