diff --git a/IM/__init__.py b/IM/__init__.py index e27452c8d..855049617 100644 --- a/IM/__init__.py +++ b/IM/__init__.py @@ -19,5 +19,5 @@ 'InfrastructureInfo', 'InfrastructureManager', 'recipe', 'request', 'REST', 'retry', 'ServiceRequests', 'SSH', 'SSHRetry', 'timedcall', 'UnixHTTPAdapter', 'uriparse', 'VirtualMachine', 'VMRC', 'xmlobject'] -__version__ = '1.6.5' +__version__ = '1.6.6' __author__ = 'Miguel Caballer' diff --git a/IM/auth.py b/IM/auth.py index b69130262..ca5a4a644 100644 --- a/IM/auth.py +++ b/IM/auth.py @@ -71,7 +71,7 @@ def getAuthInfoByID(self, auth_id): """ res = [] for auth in self.auth_list: - if auth['id'] == auth_id: + if 'id' in auth and auth['id'] == auth_id: res.append(auth) return res @@ -114,22 +114,49 @@ def compare(self, other_auth, auth_type): return True + @staticmethod + def split_line(line): + """ + Split line using ; as separator char + considering single quotes as a way to delimit + tokens. (in particular to enable using char ; inside a token) + """ + tokens = [] + token = "" + in_qoutes = False + in_dqoutes = False + for char in line: + if char == '"' and not in_qoutes: + in_dqoutes = not in_dqoutes + elif char == "'" and not in_dqoutes: + in_qoutes = not in_qoutes + elif char == ";" and not in_qoutes and not in_dqoutes: + tokens.append(token) + token = "" + else: + token += char + # Add the last token + if token.strip() != "": + tokens.append(token) + + return tokens + @staticmethod def read_auth_data(filename): """ Read a file to load the Authentication data. The file has the following format: - id = one; type = OpenNebula; host = osenserve:2633; username = user; password = pass - type = InfrastructureManager; username = user; password = pass - type = VMRC; host = http://server:8080/vmrc; username = user; password = pass + id = one; type = OpenNebula; host = oneserver:2633; username = user; password = pass + type = InfrastructureManager; username = user; password = 'pass;test' + type = VMRC; host = http://server:8080/vmrc; username = user; password = "pass';test" id = ec2; type = EC2; username = ACCESS_KEY; password = SECRET_KEY id = oshost; type = OpenStack; host = oshost:8773; username = ACCESS_KEY; key = SECRET_KEY id = occi; type = OCCI; host = occiserver:4567; username = user; password = file(/tmp/filename) id = occi; type = OCCI; proxy = file(/tmp/proxy.pem) Arguments: - - filename(str): The filename to read + - filename(str or list): The filename to read or list of auth lines Returns: a list with all the auth data """ @@ -146,7 +173,7 @@ def read_auth_data(filename): line = line.strip() if len(line) > 0 and not line.startswith("#"): auth = {} - tokens = line.split(";") + tokens = Authentication.split_line(line) for token in tokens: key_value = token.split(" = ") if len(key_value) != 2: diff --git a/changelog b/changelog index 9efe44bad..4a2626682 100644 --- a/changelog +++ b/changelog @@ -362,3 +362,10 @@ IM 1.6.5 * Homogenize Inf ID log message * Fix error cpu.count parameter is ignored in OpenStack conn. * Fix ansible_version is not available in ctxt process. + +IM 1.6.6 + * Fix authorization file format does not allow passwords that contain ";". + * Improve error message in ONE conn in case net without leases. + * Fix error using disks.free_size in connectors. + * Add retries in Azure RG deletion. + * Avoid raising error in case that one auth line does not have the type field. \ No newline at end of file diff --git a/doc/source/client.rst b/doc/source/client.rst index 209aea49c..572336524 100644 --- a/doc/source/client.rst +++ b/doc/source/client.rst @@ -130,8 +130,14 @@ this:: id = id_value ; type = value_of_type ; username = value_of_username ; password = value_of_password -Values can contain "=", and "\\n" is replaced by carriage return. The available -keys are: +Values can contain "=", and "\\n" is replaced by carriage return. +You can also delimit the values using single or double quotes (e.g. if a semicolon or some quote character + appear in a value)(from version 1.6.6):: + + id = id_value ; type = value_of_type ; username = value_of_username ; password = 'some;"password' + id = id_value ; type = value_of_type ; username = value_of_username ; password = "some;'password" + +The available keys are: * ``type`` indicates the service that refers the credential. The services supported are ``InfrastructureManager``, ``VMRC``, ``OpenNebula``, ``EC2``,, ``FogBow``, diff --git a/docker-devel/Dockerfile b/docker-devel/Dockerfile index 7bdc2ce0f..713fe1704 100644 --- a/docker-devel/Dockerfile +++ b/docker-devel/Dockerfile @@ -2,7 +2,7 @@ FROM grycap/jenkins:ubuntu16.04-im ARG BRANCH=devel MAINTAINER Miguel Caballer -LABEL version="1.6.5" +LABEL version="1.6.6" LABEL description="Container image to run the IM service. (http://www.grycap.upv.es/im)" EXPOSE 8899 8800 diff --git a/docker/Dockerfile b/docker/Dockerfile index d60e2b88d..730438b79 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ # Dockerfile to create a container with the IM service FROM ubuntu:16.04 LABEL maintainer="Miguel Caballer " -LABEL version="1.6.5" +LABEL version="1.6.6" LABEL description="Container image to run the IM service. (http://www.grycap.upv.es/im)" EXPOSE 8899 8800 @@ -18,7 +18,7 @@ RUN pip install msrest msrestazure azure-common azure-mgmt-storage azure-mgmt-co RUN apt-get update && apt-get install --no-install-recommends -y gcc libmysqld-dev libssl-dev libffi-dev libsqlite3-dev && \ pip install MySQL-python && \ pip install xmltodict && \ - pip install IM==1.6.5 && \ + pip install IM==1.6.6 && \ apt-get remove -y gcc libmysqld-dev libssl-dev libffi-dev libsqlite3-dev python-dev python-pip && \ apt-get autoremove -y && \ rm -rf /var/lib/apt/lists/* diff --git a/test/files/auth.dat b/test/files/auth.dat new file mode 100644 index 000000000..c01cdf2f5 --- /dev/null +++ b/test/files/auth.dat @@ -0,0 +1,4 @@ +type = InfrastructureManager; username = someurser; password = somepass +type = VMRC; host = http://server:8080/vmrc/vmrc; username = user; password = pass +id = one2; type = OpenNebula; host = server1.com:2633; username = user; password = pass +id = occi; type = OCCI; proxy = file(/tmp/privatekey.pem); host = https://server.com:11443 \ No newline at end of file diff --git a/test/unit/auth.py b/test/unit/auth.py new file mode 100755 index 000000000..b72cdbd91 --- /dev/null +++ b/test/unit/auth.py @@ -0,0 +1,67 @@ +#! /usr/bin/env python +# +# IM - Infrastructure Manager +# Copyright (C) 2011 - GRyCAP - Universitat Politecnica de Valencia +# +# This program 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. +# +# This program 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 this program. If not, see . + +import unittest +import os +import shutil + +from IM.auth import Authentication + + +class TestAuth(unittest.TestCase): + """ + Class to test the Authentication class + """ + + def test_auth_read(self): + auth_lines = ["""id = 1; type = InfrastructureManager; username = someuser; password = somepass """, + """id = 2; type = VMRC; username = someuser; password = somepass; """, + """id = 3; type = OpenNebula; username = someuser; password = "some;'pass" """, + """id = 4; type = EC2; username = someuser; password = 'some;"pass' """] + auth = Authentication.read_auth_data(auth_lines) + self.assertEqual(auth, [{'id': '1', 'password': "somepass", + 'type': 'InfrastructureManager', 'username': 'someuser'}, + {'id': '2', 'password': "somepass", + 'type': 'VMRC', 'username': 'someuser'}, + {'id': '3', 'password': "some;'pass", + 'type': 'OpenNebula', 'username': 'someuser'}, + {'id': '4', 'password': 'some;"pass', + 'type': 'EC2', 'username': 'someuser'}]) + + tests_path = os.path.dirname(os.path.abspath(__file__)) + shutil.copyfile(os.path.join(tests_path, "../files/privatekey.pem"), "/tmp/privatekey.pem") + auth = Authentication(Authentication.read_auth_data(os.path.join(tests_path, "../files/auth.dat"))) + auth_data = auth.getAuthInfoByID("occi") + self.assertEqual(auth_data[0]['proxy'][:37], "-----BEGIN RSA PRIVATE KEY-----\nMIIEo") + os.unlink("/tmp/privatekey.pem") + + + def test_get_auth(self): + auth_lines = ["""id = 1; type = InfrastructureManager; username = someuser; password = somepass """, + """id = 2; type = VMRC; username = someuser; password = somepass; """] + auth = Authentication(Authentication.read_auth_data(auth_lines)) + auth_data = auth.getAuthInfoByID("1") + self.assertEqual(auth_data, [{'id': '1', 'password': "somepass", + 'type': 'InfrastructureManager', 'username': 'someuser'}]) + auth_data = auth.getAuthInfo("VMRC") + self.assertEqual(auth_data, [{'id': '2', 'password': "somepass", + 'type': 'VMRC', 'username': 'someuser'}]) + + +if __name__ == '__main__': + unittest.main()