Commit 46f2aac9 authored by Xavier Thompson's avatar Xavier Thompson

Include sharedness in service info

See merge request nexedi/slapos.core!516
parents ab87a9cd f1d84e5f
......@@ -106,6 +106,7 @@ def do_info(logger, conf, local):
info = {
'software-url': instance._software_release_url,
'software-type': instance._source_reference,
'shared': bool(instance._root_slave),
'requested-state': instance._requested_state,
'instance-parameters': instance._parameter_dict,
'connection-parameters': connection_parameter_dict,
......
......@@ -1037,6 +1037,29 @@ def busy_root_partitions_list(title=None):
partitions.append(p)
return partitions
def busy_root_shared_list(title=None):
shared = []
query = 'SELECT * FROM %s WHERE asked_by==""'
args = []
if title:
query += ' AND reference=?'
args.append('_' + title)
for row in execute_db('slave', query, args):
host = execute_db('partition', 'SELECT * FROM %s WHERE reference=?', [row['hosted_by']], one=True)
if not host:
continue
for slave_dict in loads(host['slave_instance_list'].encode('utf-8')):
if slave_dict['slave_reference'] == row['reference']:
break
else:
continue
s = {}
s['url_string'] = host['software_release']
s['title'] = row['reference'][1:] # root shared are prefixed with _
s['relative_url'] = url_for('hateoas_shared', shared_reference=s['title'])
shared.append(s)
return shared
def computers_list(reference=None):
computers = []
query = 'SELECT * FROM %s'
......@@ -1066,11 +1089,14 @@ p_computer_info = p_computer_list + ' AND reference:='
def parse_query(query):
if query == p_service_list:
return busy_root_partitions_list()
return busy_root_partitions_list() + busy_root_shared_list()
elif query.startswith(p_service_info):
title = query[len(p_service_info):]
if is_valid(title):
return busy_root_partitions_list(title.strip('"'))
partition = busy_root_partitions_list(title.strip('"'))
if partition:
return partition
return busy_root_shared_list(title.strip('"'))
elif query == p_computer_list:
return computers_list()
elif query.startswith(p_computer_info):
......@@ -1084,44 +1110,81 @@ def hateoas_partitions(partition_reference):
partition = execute_db('partition', 'SELECT * FROM %s WHERE partition_reference=?', [partition_reference], one=True)
if partition is None:
abort(404)
partition['reference']=partition['partition_reference']
return hateoas_service_document(**partition, shared=0)
@app.route('/hateoas/shared/<shared_reference>', methods=['GET'])
def hateoas_shared(shared_reference):
slave_reference = '_' + shared_reference # root shared are prefixed with _ in db
shared = execute_db('slave', 'SELECT * FROM %s WHERE reference=?', [slave_reference], one=True)
if shared is None:
abort(404)
partition = execute_db('partition', 'SELECT * FROM %s WHERE reference=?', [shared['hosted_by']], one=True)
if partition is None:
abort(404)
slave_list = loads(partition['slave_instance_list'].encode('utf-8'))
for slave_dict in slave_list:
if slave_dict['slave_reference'] == slave_reference:
break
else:
abort(404)
del slave_dict['slave_title'], slave_dict['slave_reference']
software_type = slave_dict.pop('slap_software_type')
xml = dict2xml(slave_dict)
return hateoas_service_document(
reference = shared_reference,
requested_state='unused',
xml=xml,
connection_xml=shared['connection_xml'],
software_release=partition['software_release'],
software_type=software_type,
shared=1,
)
def hateoas_service_document(**kw):
# my_slap_state corresponds to requested_state, not slap_state.
return {
'_embedded': {
'_view': {
'form_id': {
'type': 'StringField',
'key': 'partition',
'default': partition['reference'],
'key': 'form_id',
'default': 'InstanceTree_viewAsHateoas',
},
'my_reference': {
'type': 'StringField',
'key': 'field_my_reference',
'default': partition['partition_reference'],
'default': kw['reference'],
},
'my_slap_state': {
'type': 'StringField',
'key': 'field_my_slap_state',
'default': partition['requested_state'],
'default': kw['requested_state'],
},
'my_text_content': {
'type': 'StringField',
'key': 'field_my_text_content',
'default': partition['xml'],
'default': kw['xml'],
},
'my_connection_parameter_list': {
'type': 'StringField',
'key': 'field_my_connection_parameter_list',
'default': partition['connection_xml'],
'default': kw['connection_xml'],
},
'my_url_string': {
'type': 'StringField',
'key': 'field_my_url_string',
'default': partition['software_release'],
'default': kw['software_release'],
},
'my_source_reference': {
'type': 'StringField',
'key': 'field_my_source_reference',
'default': partition['software_type'],
'default': kw['software_type'],
},
'my_root_slave': {
'type': 'IntegerField',
'key': 'field_my_root_slave',
'default': kw['shared'],
},
},
},
......
......@@ -663,6 +663,7 @@ class TestCliInfo(CliMixin):
instance = slapos.slap.SoftwareInstance(
_software_release_url='SR1',
_source_reference='mytype',
_root_slave=False,
_requested_state='mystate',
_connection_dict=conn_params,
_parameter_dict={'myinstanceparameter': 'value2'})
......@@ -684,6 +685,7 @@ class TestCliInfo(CliMixin):
expected = {
"software-url": instance._software_release_url,
"software-type": instance._source_reference,
'shared': instance._root_slave,
"requested-state": instance._requested_state,
"instance-parameters": instance._parameter_dict,
"connection-parameters": {
......
......@@ -451,7 +451,7 @@ class MasterMixin(BasicMixin, unittest.TestCase):
app = self.app
class TestConnectionHelper:
def GET(self, path, params=None, headers=None):
return app.get(path, query_string=params, data=data).data
return app.get(path, query_string=params, headers=headers).data
def POST(self, path, params=None, data=None,
content_type='application/x-www-form-urlencoded'):
......@@ -1217,8 +1217,9 @@ database_uri = %(rootdir)s/lib/proxy.db
)
def startProxy(self):
logfile = os.path.join(self._rootdir, 'proxy.log')
self.proxy_process = self.cliDoSlapos(
('proxy', 'start'),
('proxy', 'start', '--log-file', logfile),
method=subprocess.Popen,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
......@@ -1288,6 +1289,16 @@ class TestCliInformation(CliMasterMixin):
json.loads(output),
{'MyInstance0': 'http://sr0//', 'MyInstance1': 'http://sr1//', 'MyInstance2': 'http://sr2//'})
def test_service_list_with_shared(self):
self.format_for_number_of_partitions(1)
self.request('http://sr0//', None, 'MyHostInstance0', None)
self.request('http://sr0//', None, 'MySharedInstance1', None, shared=True)
self.request('http://sr0//', None, 'MySharedInstance2', None, shared=True)
output = self.cliDoSlapos(('service', 'list'), stderr=subprocess.DEVNULL)
self.assertEqual(
json.loads(output),
{'MyHostInstance0': 'http://sr0//', 'MySharedInstance1': 'http://sr0//', 'MySharedInstance2': 'http://sr0//'})
def test_service_info(self):
self.format_for_number_of_partitions(3)
self.request('http://sr0//', None, 'MyInstance0', None)
......@@ -1299,6 +1310,7 @@ class TestCliInformation(CliMasterMixin):
{
"software-url": "http://sr0//",
"software-type": "default",
"shared": False,
"requested-state": "started",
"instance-parameters": {},
"connection-parameters": {},
......@@ -1311,6 +1323,7 @@ class TestCliInformation(CliMasterMixin):
{
"software-url": "http://sr1//",
"software-type": "MyType1",
"shared": False,
"requested-state": "started",
"instance-parameters": {"couscous": "hello"},
"connection-parameters": {},
......@@ -1323,6 +1336,38 @@ class TestCliInformation(CliMasterMixin):
except subprocess.CalledProcessError as e:
self.assertIn('Instance MyInstance2 does not exist.', e.output)
def test_service_info_with_shared(self):
self.format_for_number_of_partitions(1)
self.request('http://sr0//', 'MyType1', 'MyHostInstance0', None)
self.request('http://sr0//', 'MyType1', 'MySharedInstance1', None, shared=True, partition_parameter_kw={'couscous': 'hello'})
self.request('http://sr0//', 'MyType1', 'MySharedInstance2', None, shared=True, partition_parameter_kw={'couscous': 'bye'})
output0 = self.cliDoSlapos(('service', 'info', 'MySharedInstance1'), stderr=subprocess.DEVNULL)
self.assertEqual(
json.loads(output0),
{
"software-url": "http://sr0//",
"software-type": "MyType1",
"shared": True,
"requested-state": "unused",
"instance-parameters": {"couscous": "hello"},
"connection-parameters": {},
"status": "unsupported",
},
)
output1 = self.cliDoSlapos(('service', 'info', 'MySharedInstance2'), stderr=subprocess.DEVNULL)
self.assertEqual(
json.loads(output1),
{
"software-url": "http://sr0//",
"software-type": "MyType1",
"shared": True,
"requested-state": "unused",
"instance-parameters": {"couscous": "bye"},
"connection-parameters": {},
"status": "unsupported",
},
)
def test_invalid_service_names(self):
invalid_names = ('"MyInstance0', 'MyInstance1"', 'My"Instance2', 'title:="MyInstance3"', 'reference:="MyInstance4"')
self.format_for_number_of_partitions(len(invalid_names))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment