diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8dada3e..0000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c74aced --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2016 The Python Packaging Authority (PyPA) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..7557376 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,9 @@ +# Include the license file +include LICENSE.txt + +# Include the data files +recursive-include data * + +# If using Python 2.6 or less, then have to include package data, even though +# it's already declared in setup.py +# include sample/*.dat diff --git a/README.md b/README.md index 4fa05a6..8c91276 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# virtshell_server -Server +VirtShell-Server Project +======================== + +VirtShell-Server is a server offering a rest api to perform provisioning of +virtual resources like virtual machines or containers. \ No newline at end of file diff --git a/ToDo.txt b/ToDo.txt new file mode 100644 index 0000000..4609ef2 --- /dev/null +++ b/ToDo.txt @@ -0,0 +1,35 @@ +Roadmap +======= ++ Security: + - Authentication and Authorization in the requests + - Authorization in the virtual resources + ++ Database + - Scripts to deploy the mongodb in two or more servers + - Partition the database and abstract that in a layer + - That abstraction layer allows us to do dynamic routing, in other words to do routing based on the configuration of what items live on what hosts, + ++ Deploy + - Scripts to deploy two or more servers with one balancer + - Run tornado in nginx + ++ Integrate + - Service to build images to virtualbox + ++ Hosts + - When a new host is added, automatically a new task should be create to install the provisioning_agent (and other agents) in the host. + ++ Amazon + - Create a separated service to interact with amazon api + ++ All modules + - Add unit tests and integration test with other services + - If the element in the database not exists return a correct status and message error + - Add logger to all modules + ++ Intances + - Add iptables for the enviroments (limit the access between machines of the same partition) + - When a new instance is created it should be added in enviroments + - When a instance is deleted it should be deleted in the host too (physically). + ++ Error Handling \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..79bc678 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..dcfb72d --- /dev/null +++ b/setup.py @@ -0,0 +1,104 @@ +"""VirtShell-Server. + +See: +https://github.com/janutechnology/VirtShell +""" + +from setuptools import setup, find_packages +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='virtshell_server', + + version='1.0.0', + + description='A sample Python project', + long_description=long_description, + + # The project's main homepage. + url='https://github.com/janutechnology/VirtShell', + + # Author details + author='Carlos Alberto Llano R.', + author_email='carlos_llano@hotmail.com', + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + 'Development Status :: 3 - Alpha', + + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: MIT License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4' + ], + + # What does your project relate to? + keywords='provisioning', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + packages=find_packages(exclude=['contrib', 'docs', 'tests']), + + # Alternatively, if you want to distribute just a my_module.py, uncomment + # this: + # py_modules=["my_module"], + + # List run-time dependencies here. These will be installed by pip when + # your project is installed. For an analysis of "install_requires" vs pip's + # requirements files see: + # https://packaging.python.org/en/latest/requirements.html + install_requires=['tornado', 'pymongo', 'pyrestful'], + + # List additional groups of dependencies here (e.g. development + # dependencies). You can install these using the following syntax, + # for example: + # $ pip install -e .[dev,test] + #extras_require={ + # 'dev': ['check-manifest'], + # 'test': ['coverage'], + #}, + + # If there are data files included in your packages that need to be + # installed, specify them here. If using Python 2.6 or less, then these + # have to be included in MANIFEST.in as well. + #package_data={ + # 'sample': ['package_data.dat'], + #}, + + # Although 'package_data' is the preferred approach, in some case you may + # need to place data files outside of your packages. See: + # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa + # In this case, 'data_file' will be installed into '/my_data' + #data_files=[('my_data', ['data/data_file'])], + + # To provide executable scripts, use entry points in preference to the + # "scripts" keyword. Entry points provide cross-platform support and allow + # pip to create the appropriate form of executable for the target platform. + #entry_points={ + # 'console_scripts': [ + # 'sample=sample:main', + # ], + #}, +) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/curl_tests.txt b/tests/curl_tests.txt new file mode 100644 index 0000000..20c9495 --- /dev/null +++ b/tests/curl_tests.txt @@ -0,0 +1,129 @@ +############################################################################################ + +# Get users +curl -s http://localhost:80/users/virtshell | jq . +curl -s http://localhost:80/users/ | jq . + +# Create a user +curl -X POST http://127.0.0.1:80/users/ -d "{\"username\": \"virtshell\", \"type\": \"administrator\", \"login\": \"user@mail.com\", \"groups\": [{\"name\": \"web_development_team\"}, {\"name\": \"production\"}]}" -H "accept:application/json" | jq . + +# Delete a user +curl -X DELETE http://127.0.0.1:80/users/virtshell | jq . + +# Update a user +curl -X PUT http://127.0.0.1:80/users/virtshell -d "{\"login\": \"user@gmail.com\"}" -H "accept:application/json" | jq . + +############################################################################################ + +# Get groups +curl -s http://localhost:80/groups/web_development_team | jq . +curl -s http://localhost:80/groups/ | jq . + +# Create a group +curl -X POST http://127.0.0.1:80/groups/ -d "{\"name\": \"web_development_team\", \"users\": [{\"user\": \"virtshell\"}, {\"user\": \"demouser\"}]}" -H "accept:application/json" | jq . + +# Delete a group +curl -X DELETE http://127.0.0.1:80/groups/web_development_team | jq . + +# Update a group +curl -X PUT http://127.0.0.1:80/groups/web_development_team -d "{\"users\": [{\"user\": \"virtshell\"}]}" -H "accept:application/json" | jq . + +############################################################################################ + +# Get provisioners +curl -s http://localhost:80/provisioners/backend-services-provisioner | jq . +curl -s http://localhost:80/provisioners/ | jq . + +# Create a provisioner +curl -X POST http://127.0.0.1:80/provisioners/ -d "{\"name\": \"backend-services-provisioner\", \"description\": \"Installs-Configures a backend server\", \"version\": \"1.5.8\", \"repository\": \"https://github.com/janutechnology/VirtShell_Provisioners_Examples.git\", \"executor\": \"run1.sh\", \"tag\": \"backend\", \"depends\": []}" -H "accept:application/json" | jq . + +# Delete a provisioner +curl -X DELETE http://127.0.0.1:80/provisioners/backend-services-provisioner | jq . + +# Update a provisioner +curl -X PUT http://127.0.0.1:80/provisioners/backend-services-provisioner -d "{\"depends\": [\"database_co\"]}" -H "accept:application/json" | jq . + +############################################################################################ + +# Get Instances +curl -s http://localhost:80/instances/transactional_log | jq . +curl -s http://localhost:80/instances/c2380fd6-ad13-4cf8-bf8e-5feeaaf74f3e | jq . +curl -s http://localhost:80/instances/ | jq . + +# Create an Instance +curl -X POST http://127.0.0.1:80/instances/ -d "{\"name\": \"transactional_log\", \"memory\": 1024, \"cpus\": 2, \"hdsize\": \"2GB\", \"description\": \"Server transactional only for store logs\", \"enviroment\": \"bigdata_test_01\", \"operating_system\": \"ubuntu_server_14.04.2_amd64\", \"provisioner\": \"all_backend\", \"host_type\": \"GeneralPurpose\", \"driver\": \"docker\"}" -H "accept:application/json" | jq . + +# Delete an Instance +curl -X DELETE http://127.0.0.1:80/instances/transactional_log | jq . + +# Update an Instance +curl -X PUT http://127.0.0.1:80/instances/transactional_log -d "{\"ipv4\": \"172.17.0.2\"}" -H "accept:application/json" | jq . + +############################################################################################ + +# Get Enviroments +curl -s http://localhost:80/enviroments/bigdata_test_01 | jq . +curl -s http://localhost:80/enviroments/ | jq . + +# Create an Enviroment +curl -X POST http://127.0.0.1:80/enviroments/ -d "{\"name\":\"development\",\"description\":\"Collection of servers for development.\", \"partition\": \"development_co\", \"users\":[{\"login\":\"development_user\"},{\"login\":\"guest\"}]}" -H "accept:application/json" | jq . + +# Delete an Instance +curl -X DELETE http://127.0.0.1:80/enviroments/bigdata_test_01 | jq . + +############################################################################################ + +# Get Partitions +curl -s http://localhost:80/partitions/development_co | jq . +curl -s http://localhost:80/partitions/ | jq . + +# Create a Partition +curl -X POST http://127.0.0.1:80/partitions/ -d "{\"name\":\"development_co\",\"description\":\"Collection of servers oriented to development team in Colombia.\"}" -H "accept:application/json" | jq . + +curl -X POST http://127.0.0.1:80/partitions/ -d "{\"name\":\"develotment_pe\",\"description\":\"Collection of servers oriented to development team in Mexico.\"}" -H "accept:application/json" | jq . + +# Update an Partition +curl -X PUT http://127.0.0.1:80/partitions/development_mx -d "{\"description\":\"Collection of servers oriented to development team in Mexico DF.\"}" -H "accept:application/json" | jq . + +# Delete an Partition +curl -X DELETE http://127.0.0.1:80/partitions/development_co | jq . + +# Add a host +curl -sv -X PUT http://127.0.0.1:80/partition/development_co/host/host-06-pdn | jq . + +############################################################################################ + +# Get hosts +curl -s http://localhost:80/hosts/host-05-pdn | jq . +curl -s http://localhost:80/hosts/ | jq . + +# Create a host +curl -X POST http://127.0.0.1:80/hosts/ -d "{\"name\": \"host-01\", \"os\": \"ubuntu-14.04.4-server-amd64\", \"memory\": \"2GB\", \"partition\":\"development_co\", \"type\": \"GeneralPurpose\"}" -H "accept:application/json" | jq . + +# Delete a host +curl -X DELETE http://127.0.0.1:80/hosts/host-12-pdn | jq . + +# Update a host +curl -X PUT http://127.0.0.1:80/hosts/host-05-pdn -d "{\"os\": \"Ubuntu_18.04_3.5.0-23.x86_64\", \"memory\": \"16GB\"}" -H "accept:application/json" | jq . + +############################################################################################ + +# Get tasks +curl -s http://localhost:80/tasks/status | jq . +curl -s http://localhost:80/tasks/status/pending | jq . +curl -s http://localhost:80/tasks/11ef00f7-d923-47f3-9d62-1a38caf33e62 | jq . +curl -s http://localhost:80/tasks/ | jq . + +# Create a new tasks +curl -X POST http://127.0.0.1:80/tasks/ -d "{\"description\": \"clone virtual machine database_01\", \"status\" : \"pending\"}" -H "accept:application/json" | jq . + +# Update a task +curl -X PUT http://127.0.0.1:80/tasks/11ef00f7-d923-47f3-9d62-1a38caf33e62 -d "{\"log\": \"Feb 20 10:40:33 Janu dhclient: DHCPREQUEST of 192.168.0.14 on wlan0 to 192.168.0.1 port 67 Feb 20 10:40:33 Janu dhclient: DHCPACK of 192.168.0.14 from 192.168.0.1 Feb 20 10:40:33 Janu dhclient: bound to 192.168.0.14 -- renewal in 1525 seconds.\"}" -H "accept:application/json" | jq . + +# Delete a task +curl -X DELETE http://127.0.0.1:80/tasks/cfca1a23-a34a-4ca4-9098-5e543fe06e06 | jq . + +############################################################################################ + +# Get files +curl -s http://localhost:80/files/ | jq . \ No newline at end of file diff --git a/tests/files/dockerfile_centos_server_7 b/tests/files/dockerfile_centos_server_7 new file mode 100644 index 0000000..3c27345 --- /dev/null +++ b/tests/files/dockerfile_centos_server_7 @@ -0,0 +1,36 @@ +FROM centos:7 + + MAINTAINER Carlos Llano + + RUN yum update -y + RUN yum install -y openssh-server sudo git which openssh-clients openssh + + RUN mkdir -p /var/run/sshd ; chmod -rx /var/run/sshd + RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key + RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key + + RUN useradd -s /bin/bash -m virtshell + RUN echo "virtshell:virtshell" | chpasswd + RUN echo "virtshell ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + + RUN echo "root:virtshell" | chpasswd + RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config + RUN sed -ri 's/#UsePAM no/UsePAM no/g' /etc/ssh/sshd_config + RUN sed -i "s/PermitRootLogin without-password/PermitRootLogin yes/" /etc/ssh/sshd_config + RUN /bin/sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd + + RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" > /etc/default/local + ENV NOTVISIBLE "in users profile" + RUN echo "export VISIBLE=now" >> /etc/profile + + EXPOSE 22 + CMD ["/usr/sbin/sshd", "-D"] + + RUN yum install -y epel-release + RUN yum install -y python34 + RUN yum -y update + RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 + + RUN pip3 install virtshell_commands + + CMD ["/usr/sbin/init"] diff --git a/tests/files/dockerfile_ubuntu_server_14.04 b/tests/files/dockerfile_ubuntu_server_14.04 new file mode 100644 index 0000000..c5871f8 --- /dev/null +++ b/tests/files/dockerfile_ubuntu_server_14.04 @@ -0,0 +1,46 @@ +FROM ubuntu:14.04 +MAINTAINER Carlos Llano + +RUN locale-gen en_US.UTF-8 +RUN dpkg-reconfigure locales + +RUN sed 's/#$ModLoad imudp/$ModLoad imudp/' -i /etc/rsyslog.conf +RUN sed 's/#$UDPServerRun 514/$UDPServerRun 514/' -i /etc/rsyslog.conf +RUN sed 's/#$ModLoad imtcp/$ModLoad imtcp/' -i /etc/rsyslog.conf +RUN sed 's/#$InputTCPServerRun 514/$InputTCPServerRun 514/' -i /etc/rsyslog.conf +EXPOSE 514/tcp 514/udp +CMD ["/usr/sbin/rsyslogd", "-dn", "-f", "/etc/rsyslog.conf"] + +RUN apt-get update -y +RUN apt-get install -y openssh-server git + +RUN mkdir /var/run/sshd +RUN useradd -s /bin/bash -m virtshell +RUN echo "virtshell:virtshell" | chpasswd +RUN echo "virtshell ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +RUN echo "root:virtshell" | chpasswd +RUN sed -i "s/PermitRootLogin without-password/PermitRootLogin yes/" /etc/ssh/sshd_config + +RUN sed "s@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g" -i /etc/pam.d/sshd + +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile + +EXPOSE 22 +CMD ["/usr/sbin/sshd", "-D"] + +RUN apt-get install -y python3-setuptools python3-pip +RUN pip3 install virtshell_commands + +RUN echo 'export LC_ALL=C' >> ~/.bashrc +CMD ["source .bashrc"] + +RUN apt-get install -y supervisor +RUN mkdir -p /var/log/supervisor +RUN printf '[supervisord]\nnodaemon=true\n\n' >> /etc/supervisor/conf.d/supervisord.conf +RUN printf '[program:sshd]\nautostart=true\ncommand=/usr/sbin/sshd -D' >> /etc/supervisor/conf.d/supervisord.conf + +CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf + +RUN apt-get update --fix-missing \ No newline at end of file diff --git a/tests/test_mongodb.py b/tests/test_mongodb.py new file mode 100644 index 0000000..5ed6d5d --- /dev/null +++ b/tests/test_mongodb.py @@ -0,0 +1,29 @@ +from pymongo import MongoClient +from bson.objectid import ObjectId + +CLIENT = MongoClient("mongodb://localhost:27017") +mongodb = CLIENT.virtshell_server +for host in mongodb.hosts.find(): + print (host) + +new_host = {"uuid": "ab8076c0-db91-11e2-82ce-0002a5d5c51b", + "name": "host-01-pdn", + "os": "Ubuntu_12.04_3.5.0-23.x86_64", + "memory": "16GB", + "capacity": "120GB", + "enabled": "true", + "type" : "GeneralPurpose", + "local_ipv4": "15.54.88.19", + "local_ipv6": "ff06:0:0:0:0:0:0:c3", + "public_ipv4": "10.54.88.19", + "public_ipv6": "yt06:0:0:0:0:0:0:c3"} + +host_id = mongodb.hosts.insert_one(new_host).inserted_id +print(type(host_id)) +if isinstance(host_id, ObjectId): + print("kkkkk") +print(host_id) + +for host in mongodb.hosts.find(): + print (host) + diff --git a/virtshell_server/__init__.py b/virtshell_server/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtshell_server/config.py b/virtshell_server/config.py new file mode 100644 index 0000000..31b37fe --- /dev/null +++ b/virtshell_server/config.py @@ -0,0 +1,11 @@ +""" +VirtShell-Server High Level Server Configurations +""" +DATABASE_PROVIDER = "mongodb" +MONGODB = "mongodb://localhost:27017" +DATABASE_NAME = "virtshell_server_db" +PORT = 80 +LOGGER = "syslog" #file +LOG_FILE = "/var/janu/log/virtshell_server.log" +URL_STATIC_FILES = "http://192.168.56.103/file/" +PATH_STATIC_FILES = "/var/janu/files/" diff --git a/virtshell_server/errors.py b/virtshell_server/errors.py new file mode 100644 index 0000000..a8d58b0 --- /dev/null +++ b/virtshell_server/errors.py @@ -0,0 +1,63 @@ +""" +VirtShell-Server Errors +""" + +class VirtShellServerError(Exception): + def __init__(self, message, code): + self.message = message + self.code = code + super(Exception, self).__init__(message) + +# 400 +class MissingField(VirtShellServerError): + def __init__(self, field): + IndicoError.__init__(self, + "%s field was not provided" % field, + 400 + ) + +class WrongFieldType(VirtShellServerError): + def __init__(self, field, _given, _required): + IndicoError.__init__(self, + "Field %s - %s is type %s but should be %s" % + (field, _given, type(_given), _required), + 400 + ) + +class InvalidJSON(VirtShellServerError): + def __init__(self): + IndicoError.__init__(self, + "No JSON object could be decoded.", + 400 + ) + +# 401 +class AuthError(VirtShellServerError): + def __init__(self): + IndicoError.__init__(self, + "User not authenticated", + 401 + ) + +# 404 +class RouteNotFound(VirtShellServerError): + def __init__(self, action): + IndicoError.__init__(self, + "%s route could not be found" % action, + 404 + ) + +# 500 +class MongoError(VirtShellServerError): + def __init__(self, message): + IndicoError.__init__(self, + message, + 500 + ) + +class ServerError(VirtShellServerError): + def __init__(self): + IndicoError.__init__(self, + "we screwed up and have some debugging to do", + 500 + ) diff --git a/virtshell_server/logger.py b/virtshell_server/logger.py new file mode 100644 index 0000000..546a1c1 --- /dev/null +++ b/virtshell_server/logger.py @@ -0,0 +1,26 @@ +import logging +import logging.handlers +from config import LOGGER +from config import LOG_FILE + +def get_logger(LoggerName): + # Create logger + logger = logging.getLogger(LoggerName) + logger.setLevel(logging.INFO) + + # Create handler + if LOGGER == "file": + handler = logging.FileHandler(LOG_FILE) + else: + handler = logging.handlers.SysLogHandler(address='/dev/log') + + #handler = SysLogHandler(address='/dev/log') + handler.setLevel(logging.INFO) + # Create formatter + formatter = logging.Formatter('%(asctime)s %(name)s ' + '%(levelname)s %(message)s') + # Add formatter and handler + handler.setFormatter(formatter) + logger.addHandler(handler) + + return logger diff --git a/virtshell_server/managment/__init__.py b/virtshell_server/managment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtshell_server/managment/enviroments.py b/virtshell_server/managment/enviroments.py new file mode 100644 index 0000000..6b8f775 --- /dev/null +++ b/virtshell_server/managment/enviroments.py @@ -0,0 +1,19 @@ +import uuid +from managment.enviroments_repository import EnviromentsRepository + +class Enviroments(object): + def __init__(self): + self.enviroments_repository = EnviromentsRepository() + + def get_all_enviroments(self): + return self.enviroments_repository.get_all_enviroments() + + def get_enviroment(self, name): + return self.enviroments_repository.get_enviroment(name) + + def create_enviroment(self, enviroment): + enviroment['uuid'] = str(uuid.uuid4()) + return self.enviroments_repository.create_enviroment(enviroment) + + def delete_enviroment(self, name): + return self.enviroments_repository.delete_enviroment(name) \ No newline at end of file diff --git a/virtshell_server/managment/enviroments_api.py b/virtshell_server/managment/enviroments_api.py new file mode 100644 index 0000000..b3d38b6 --- /dev/null +++ b/virtshell_server/managment/enviroments_api.py @@ -0,0 +1,50 @@ +import json +import tornado.web +import tornado.ioloop +from managment.enviroments import Enviroments + +class EnviromentsHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.enviroments = Enviroments() + self.logger = logger + + def get(self, name=None): + self.logger.info("enviroments GET " + name) + if name: + result = self.enviroments.get_enviroment(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.enviroments.get_all_enviroments() + if result['status'] == 'ok': + enviroments = result['documents'] + response = {'enviroments': enviroments} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("enviroments POST " + name) + enviroment = tornado.escape.json_decode(self.request.body) + result = self.enviroments.create_enviroment(enviroment) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("enviroments DELETE " + name) + if name: + result = self.enviroments.delete_enviroment(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +EnviromentsResources = (r'/enviroments/(.*)', EnviromentsHandler) \ No newline at end of file diff --git a/virtshell_server/managment/enviroments_repository.py b/virtshell_server/managment/enviroments_repository.py new file mode 100644 index 0000000..fc9af39 --- /dev/null +++ b/virtshell_server/managment/enviroments_repository.py @@ -0,0 +1,19 @@ +import json +import managment +from storage import StorageFactory + +class EnviromentsRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('enviroments') + + def create_enviroment(self, enviroment): + return self.storage_db.create(enviroment) + + def get_all_enviroments(self): + return self.storage_db.get() + + def get_enviroment(self, name): + return self.storage_db.get("name", name) + + def delete_enviroment(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/managment/hosts.py b/virtshell_server/managment/hosts.py new file mode 100644 index 0000000..46dfb97 --- /dev/null +++ b/virtshell_server/managment/hosts.py @@ -0,0 +1,28 @@ +import uuid +from managment.partitions import Partitions +from managment.hosts_repository import HostsRepository + +class Hosts(object): + def __init__(self): + self.hosts_repository = HostsRepository() + + def get_all_hosts(self): + return self.hosts_repository.get_all_hosts() + + def get_host(self, name): + return self.hosts_repository.get_host(name) + + def create_host(self, host): + partitions = Partitions() + result = partitions.add_host(host['partition'], host['name']) + if result['status'] == 'ok': + host['uuid'] = str(uuid.uuid4()) + return self.hosts_repository.create_host(host) + else: + return {"add_host": "error", "reason": result['reason']} + + def delete_host(self, name): + return self.hosts_repository.delete_host(name) + + def update_host(self, name, host): + return self.hosts_repository.update_host(name, host) \ No newline at end of file diff --git a/virtshell_server/managment/hosts_api.py b/virtshell_server/managment/hosts_api.py new file mode 100644 index 0000000..4f66cc9 --- /dev/null +++ b/virtshell_server/managment/hosts_api.py @@ -0,0 +1,64 @@ +import json +import tornado.web +import tornado.ioloop +import logger +from managment.hosts import Hosts + +class HostsHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.hosts = Hosts() + self.logger = logger + + def get(self, name=None): + self.logger.info("hosts GET " + name) + if name: + result = self.hosts.get_host(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.hosts.get_all_hosts() + if result['status'] == 'ok': + hosts = result['documents'] + response = {'hosts': hosts} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("hosts POST " + name) + host = tornado.escape.json_decode(self.request.body) + result = self.hosts.create_host(host) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("hosts PUT " + name) + if name: + host = tornado.escape.json_decode(self.request.body) + result = self.hosts.update_host(name, host) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("hosts DELETE " + name) + if name: + result = self.hosts.delete_host(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +HostsResources = (r'/hosts/(.*)', HostsHandler) \ No newline at end of file diff --git a/virtshell_server/managment/hosts_repository.py b/virtshell_server/managment/hosts_repository.py new file mode 100644 index 0000000..36502ce --- /dev/null +++ b/virtshell_server/managment/hosts_repository.py @@ -0,0 +1,22 @@ +import json +import managment +from storage import StorageFactory + +class HostsRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('hosts') + + def create_host(self, host): + return self.storage_db.create(host) + + def get_all_hosts(self): + return self.storage_db.get() + + def get_host(self, name): + return self.storage_db.get("name", name) + + def update_host(self, name, host): + return self.storage_db.update("name", name, host) + + def delete_host(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/managment/instances.py b/virtshell_server/managment/instances.py new file mode 100644 index 0000000..98ddc1f --- /dev/null +++ b/virtshell_server/managment/instances.py @@ -0,0 +1,48 @@ +import uuid +import datetime +from managment.tasks import Tasks +from managment.instances_repository import InstancesRepository + +class Instances(object): + def __init__(self): + self.instances_repository = InstancesRepository() + + def get_all_instances(self): + return self.instances_repository.get_all_instances() + + def get_instance(self, name): + return self.instances_repository.get_instance(name) + + def get_instance_by_uuid(self, uuid): + return self.instances_repository.get_instance_by_uuid(uuid) + + def create_instance(self, instance): + instance['uuid'] = str(uuid.uuid4()) + instance['status'] = 'pending' + task_uuid = self._create_task("Create a new instance %s using driver %s" + % (instance['name'],instance['driver']), + instance['uuid']) + return self.instances_repository.create_instance(instance) + + def delete_instance(self, name): + return self.instances_repository.delete_instance(name) + + def update_instance(self, name, instance): + return self.instances_repository.update_instance(name, instance) + + def _create_task(self, description, instance_uuid): + task = {} + task['description'] = description + task['status'] = 'pending' + task['date'] = datetime.datetime.now().time().isoformat() + task['status_history'] = [] + task['log'] = "received in tasks" + task['type'] = "create_instance" + task['object_uuid'] = instance_uuid + tasks = Tasks() + result = tasks.create_task(task) + if result['status'] == 'ok': + return result['uuid'] + else: + return result + return task \ No newline at end of file diff --git a/virtshell_server/managment/instances_api.py b/virtshell_server/managment/instances_api.py new file mode 100644 index 0000000..c309f88 --- /dev/null +++ b/virtshell_server/managment/instances_api.py @@ -0,0 +1,71 @@ +import re +import json +import tornado.web +import tornado.ioloop +import logger +from managment.instances import Instances + +UUID_REGULAR_EXPRESION = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" + +class InstancesHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.instances = Instances() + self.logger = logger + + def get(self, parameter=None): + self.logger.info("instances GET " + parameter) + if parameter: + pattern = re.compile(UUID_REGULAR_EXPRESION) + if pattern.match(parameter): + result = self.instances.get_instance_by_uuid(parameter) + else: + result = self.instances.get_instance(parameter) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.instances.get_all_instances() + if result['status'] == 'ok': + instances = result['documents'] + response = {'instances': instances} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("instances POST " + name) + instance = tornado.escape.json_decode(self.request.body) + result = self.instances.create_instance(instance) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error"} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("instances PUT " + name) + if name: + instance = tornado.escape.json_decode(self.request.body) + result = self.instances.update_instance(name, instance) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("instances DELETE " + name) + if name: + result = self.instances.delete_instance(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +InstancesResources = (r'/instances/(.*)', InstancesHandler) \ No newline at end of file diff --git a/virtshell_server/managment/instances_repository.py b/virtshell_server/managment/instances_repository.py new file mode 100644 index 0000000..cd3af1c --- /dev/null +++ b/virtshell_server/managment/instances_repository.py @@ -0,0 +1,25 @@ +import json +import managment +from storage import StorageFactory + +class InstancesRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('instances') + + def create_instance(self, instances): + return self.storage_db.create(instances) + + def get_all_instances(self): + return self.storage_db.get() + + def get_instance(self, name): + return self.storage_db.get("name", name) + + def get_instance_by_uuid(self, uuid): + return self.storage_db.get("uuid", uuid) + + def update_instance(self, name, instances): + return self.storage_db.update("name", name, instances) + + def delete_instance(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/managment/partitions.py b/virtshell_server/managment/partitions.py new file mode 100644 index 0000000..2e45fc1 --- /dev/null +++ b/virtshell_server/managment/partitions.py @@ -0,0 +1,25 @@ +import uuid +from managment.partitions_repository import PartitionsRepository + +class Partitions(object): + def __init__(self): + self.partitions_repository = PartitionsRepository() + + def get_all_partitions(self): + return self.partitions_repository.get_all_partitions() + + def get_partition(self, name): + return self.partitions_repository.get_partition(name) + + def create_partition(self, partition): + partition['uuid'] = str(uuid.uuid4()) + return self.partitions_repository.create_partition(partition) + + def delete_partition(self, name): + return self.partitions_repository.delete_partition(name) + + def update_partition(self, name, partition): + return self.partitions_repository.update_partition(name, partition) + + def add_host(self, name, host): + return self.partitions_repository.add_host(name, host) \ No newline at end of file diff --git a/virtshell_server/managment/partitions_api.py b/virtshell_server/managment/partitions_api.py new file mode 100644 index 0000000..35d417a --- /dev/null +++ b/virtshell_server/managment/partitions_api.py @@ -0,0 +1,83 @@ +import json +import tornado.web +import tornado.ioloop +import config as CONFIG +from managment.partitions import Partitions + +class PartitionsHostHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.partitions = Partitions() + self.logger = logger + + def put(self, name=None, host=False): + self.logger.info("partitions PUT name: " + name + "host: " + host) + if name and host: + result = self.partitions.add_host(name, host) + if result['status'] == 'ok': + response = {"add_host": "success"} + else: + response = {"add_host": "error", "reason": result['reason']} + elif not name and not host_name: + response = {"update": "error", "reason": "missing name and host parameters"} + return self.write(json.dumps(response)) + +class PartitionsHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.partitions = Partitions() + self.logger = logger + + def get(self, name=None): + self.logger.info("partitions GET " + name) + if name: + result = self.partitions.get_partition(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.partitions.get_all_partitions() + if result['status'] == 'ok': + partitions = result['documents'] + response = {'partitions': partitions} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("partitions POST") + partition = tornado.escape.json_decode(self.request.body) + result = self.partitions.create_partition(partition) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("partitions PUT " + name) + if name: + partition = tornado.escape.json_decode(self.request.body) + result = self.partitions.update_partition(name, partition) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("partitions DELETE " + name) + if name: + result = self.partitions.delete_partition(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + +PartitionsHostResources = (r'/partition/(.+)/host/(.+)', PartitionsHostHandler) +PartitionsResources = (r'/partitions/(.*)', PartitionsHandler) diff --git a/virtshell_server/managment/partitions_repository.py b/virtshell_server/managment/partitions_repository.py new file mode 100644 index 0000000..15d8718 --- /dev/null +++ b/virtshell_server/managment/partitions_repository.py @@ -0,0 +1,30 @@ +import json +import managment +from storage import StorageFactory + +class PartitionsRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('partitions') + + def create_partition(self, partition): + return self.storage_db.create(partition) + + def get_all_partitions(self): + return self.storage_db.get() + + def get_partition(self, name): + return self.storage_db.get("name", name) + + def update_partition(self, name, host): + return self.storage_db.update("name", name, host) + + def delete_partition(self, name): + return self.storage_db.delete("name", name) + + def add_host(self, name, host): + result = self.get_partition(name) + hosts_list = [] + if 'hosts' in result['document']: + hosts_list = result['document']['hosts'] + hosts_list.append(host) + return self.storage_db.update("name", name, {'hosts': hosts_list}) \ No newline at end of file diff --git a/virtshell_server/managment/properties.py b/virtshell_server/managment/properties.py new file mode 100644 index 0000000..ba62331 --- /dev/null +++ b/virtshell_server/managment/properties.py @@ -0,0 +1,10 @@ +from managment.properties_repository import PropertiesRepository + +class Properties(object): + def __init__(self): + self.properties_repository = PropertiesRepository() + + def get_property(self, property_name, instance, enviroment): + return self.properties_repository.get_property(property_name, + instance, + enviroment) \ No newline at end of file diff --git a/virtshell_server/managment/properties_api.py b/virtshell_server/managment/properties_api.py new file mode 100644 index 0000000..b469ed5 --- /dev/null +++ b/virtshell_server/managment/properties_api.py @@ -0,0 +1,24 @@ +import json +import tornado.web +import tornado.ioloop +import logger +from managment.properties import Properties + +class PropertiesHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.properties = Properties() + self.logger = logger + + def get(self, property_name=None, instance=None, enviroment=None): + self.logger.info("properties GET property_name: " + property_name+ " instance: " + instance + " enviroment: " + enviroment) + if property_name and instance and enviroment: + result = self.properties.get_property(property_name, instance, enviroment) + if result['status'] == 'ok': + response = result['property'] + else: + response = {'error': result['reason']} + else: + response = {"get": "error", "reason": "missing parameters"} + return self.write(json.dumps(response)) + +PropertiesResources = (r'/properties/(.*)/instance/(.*)/enviroment/(.*)', PropertiesHandler) \ No newline at end of file diff --git a/virtshell_server/managment/properties_repository.py b/virtshell_server/managment/properties_repository.py new file mode 100644 index 0000000..0b922ad --- /dev/null +++ b/virtshell_server/managment/properties_repository.py @@ -0,0 +1,10 @@ +import json +import managment +from storage import StorageFactory + +class PropertiesRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('instances') + + def get_property(self, property_name, instance, enviroment): + return self.storage_db.get_property(property_name, instance, enviroment) \ No newline at end of file diff --git a/virtshell_server/managment/tasks.py b/virtshell_server/managment/tasks.py new file mode 100644 index 0000000..b55a1c4 --- /dev/null +++ b/virtshell_server/managment/tasks.py @@ -0,0 +1,56 @@ +import uuid +import datetime +from managment.tasks_repository import TasksRepository + +class Tasks(object): + def __init__(self): + self.tasks_repository = TasksRepository() + + def get_all_tasks(self): + return self.tasks_repository.get_all_tasks() + + def get_task(self, uuid): + return self.tasks_repository.get_task(uuid) + + def get_tasks_by_status(self, status_name): + return self.tasks_repository.get_tasks_by_status(status_name) + + def create_task(self, task): + task['uuid'] = str(uuid.uuid4()) + result = self.tasks_repository.create_task(task) + result['uuid'] = task['uuid'] + return result + + def update_task(self, uuid, task): + result = self.get_task(uuid) + + if result['status'] == 'ok': + current_task = result['document'] + else: + return result + + current_status = current_task['status'] + current_status_history = current_task['status_history'] + current_date_status = current_task['date'] + current_log = current_task['log'] + + new_status = task['status'] + new_date_status = datetime.datetime.now().time().isoformat() + new_log = task['log'] + + current_status_history.append( [ current_status, + current_date_status, + current_log ] ) + + current_task['status'] = new_status + current_task['date'] = new_date_status + current_task['log'] = new_log + current_task['status_history'] = current_status_history + + return self.tasks_repository.update_task(uuid, current_task) + + def delete_task(self, name): + return self.tasks_repository.delete_task(name) + + def delete_all_tasks(self): + return self.tasks_repository.delete_all_tasks() \ No newline at end of file diff --git a/virtshell_server/managment/tasks_api.py b/virtshell_server/managment/tasks_api.py new file mode 100644 index 0000000..df3a099 --- /dev/null +++ b/virtshell_server/managment/tasks_api.py @@ -0,0 +1,87 @@ +import json +import logger +import tornado.web +import tornado.ioloop +from managment.tasks import Tasks + +class StatusTasksHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.tasks = Tasks() + self.logger = logger + + def get(self, status_name=None): + self.logger.info("statustasks GET " + status_name) + if status_name: + result = self.tasks.get_tasks_by_status(status_name) + if result['status'] == 'ok': + tasks = result['documents'] + response = {'tasks': tasks} + else: + response = {'error': result['reason']} + else: + response = {"tasks_by_status": "error", "reason": "missing status name parameter"} + return self.write(json.dumps(response)) + +class TasksHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.tasks = Tasks() + self.logger = logger + + def get(self, uuid=None): + self.logger.info("tasks GET " + str(uuid)) + if uuid: + result = self.tasks.get_task(uuid) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.tasks.get_all_tasks() + if result['status'] == 'ok': + tasks = result['documents'] + response = {'tasks': tasks} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, uuid=None): + self.logger.info("tasks POST " + str(uuid)) + task = tornado.escape.json_decode(self.request.body) + result = self.tasks.create_task(task) + if result['status'] == 'ok': + response = {"create": "success", "task_id": result['uuid']} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, uuid=None): + self.logger.info("tasks PUT " + str(uuid)) + if uuid: + task = tornado.escape.json_decode(self.request.body) + result = self.tasks.update_task(uuid, task) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing uuid parameter"} + return self.write(json.dumps(response)) + + def delete(self, uuid=None): + self.logger.info("tasks DELETE " + str(uuid)) + if uuid: + result = self.tasks.delete_task(uuid) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + result = self.tasks.delete_all_tasks() + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + +StatusTasksResources = (r'/tasks/status/(.*)', StatusTasksHandler) +TasksResources = (r'/tasks/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})*', TasksHandler) diff --git a/virtshell_server/managment/tasks_repository.py b/virtshell_server/managment/tasks_repository.py new file mode 100644 index 0000000..402016f --- /dev/null +++ b/virtshell_server/managment/tasks_repository.py @@ -0,0 +1,28 @@ +import json +import managment +from storage import StorageFactory + +class TasksRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('tasks') + + def get_all_tasks(self): + return self.storage_db.get() + + def get_task(self, uuid): + return self.storage_db.get("uuid", uuid) + + def get_tasks_by_status(self, status_name): + return self.storage_db.get_all_tasks_by_status(status_name) + + def create_task(self, task): + return self.storage_db.create(task) + + def update_task(self, uuid, task): + return self.storage_db.update("uuid", uuid, task) + + def delete_task(self, uuid): + return self.storage_db.delete("uuid", uuid) + + def delete_all_tasks(self): + return self.storage_db.delete_all() \ No newline at end of file diff --git a/virtshell_server/provisioning/__init__.py b/virtshell_server/provisioning/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtshell_server/provisioning/files.py b/virtshell_server/provisioning/files.py new file mode 100644 index 0000000..a40f7ee --- /dev/null +++ b/virtshell_server/provisioning/files.py @@ -0,0 +1,109 @@ +import os +import json +import uuid +import config +from base64 import b64encode +from provisioning.files_repository import FilesRepository + +class Files(object): + def __init__(self): + self.files_repository = FilesRepository() + self.static_path = config.PATH_STATIC_FILES + self.url_static_path = config.URL_STATIC_FILES + + def get_all_files(self): + return self.files_repository.get_all_files() + + def get_file(self, name): + return self.files_repository.get_file(name) + + def get_content(self, file_name): + result = {} + try: + file_json = self.get_file(file_name) + file_path = file_json['document']['location'] + with open(file_path, 'rb') as content_file: + byte_content = content_file.read() + + base64_bytes = b64encode(byte_content) + base64_string = base64_bytes.decode("utf-8") + + result['status'] = 'ok' + result['content_file'] = base64_string + return result + except Exception as e: + print(e) + result['status'] = 'error' + result['reason'] = e + return result + + def create_file(self, file_content, file_name, permissions): + if not os.path.exists(self.static_path): + os.makedirs(directory) + + file_path = os.path.join(self.static_path, file_name) + if os.path.isfile(file_path): + result = {} + result['status'] = 'error' + result['reason'] = 'file already exists' + return result + + file_handler = open(file_path, 'w') + file_handler.write(file_content) + file_handler.close() + + file_data = {} + file_data['uuid'] = str(uuid.uuid4()) + file_data['name'] = file_name + file_data['location'] = file_path + file_data['permissions'] = permissions + file_data['uri'] = os.path.join(self.url_static_path, file_name) + + result = self.files_repository.create_file(file_data) + if result['status'] == 'ok': + result['uri'] = file_data['uri'] + return result + + def delete_file(self, name): + file_json = self.get_file(name) + + if file_json['status'] == 'ok': + file_path = file_json['document']['location'] + + if os.path.isfile(file_path): + os.remove(file_path) + path, filename = os.path.split(file_path) + return self.files_repository.delete_file(name) + else: + result = {} + result['status'] = 'error' + result['reason'] = 'file not exists in location ' + file_path + return result + else: + return file_json + + def update_file(self, file_name, file_content, permissions): + file_json = self.get_file(file_name) + + if file_json['status'] == 'ok': + if file_content != None: + file_path = file_json['document']['location'] + + if os.path.isfile(file_path): + file_handler = open(file_path, 'w') + file_handler.write(file_content.decode('utf-8')) + file_handler.close() + else: + result['status'] = 'error' + result['reason'] = 'file not exists in location ' + file_path + + result = {} + if permissions != None: + file_data = {} + file_data['permissions'] = permissions + return self.files_repository.update_file(file_name, file_data) + else: + result['status'] = 'ok' + return result + else: + return file_json \ No newline at end of file diff --git a/virtshell_server/provisioning/files_api.py b/virtshell_server/provisioning/files_api.py new file mode 100644 index 0000000..f3c9b20 --- /dev/null +++ b/virtshell_server/provisioning/files_api.py @@ -0,0 +1,101 @@ +import os +import json +import tornado.web +import tornado.ioloop +from provisioning.files import Files + +class FileHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.files = Files() + self.logger = logger + + def get(self, name=None): + self.logger.info("file GET name: " + name) + if name: + result = self.files.get_content(name) + if result['status'] == 'ok': + response = result['content_file'] + else: + response = {'error': result['reason']} + else: + response = {"get": "error", "reason": "missing name"} + return self.write(json.dumps(response)) + +class FilesHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.files = Files() + self.logger = logger + + def get(self, uri=None): + self.logger.info("files GET " + uri) + if uri: + result = self.files.get_file(uri) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.files.get_all_files() + if result['status'] == 'ok': + files = result['documents'] + response = {'files': files} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("files POST " + name) + try: + file_content = self.request.files['file'][0]['body'].decode("utf-8") + file_name = self.request.files['file'][0]['filename'] + except KeyError: + response = {"create": "error", "reason": "missing file"} + return self.write(json.dumps(response)) + + permissions = self.get_argument("permissions", + default="xwrxwrxwr", + strip=False) + + result = self.files.create_file(file_content, + file_name, + permissions) + if result['status'] == 'ok': + response = {"create": "success", "uri": result['uri']} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + + def put(self, name=None): + self.logger.info("files PUT " + name) + try: + file_content = self.request.files['file'][0]['body'] + except KeyError: + file_content = None + permissions = self.get_argument("permissions", + default=None, + strip=False) + if name: + result = self.files.update_file(name, file_content, permissions) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("files DELETE " + name) + if name: + result = self.files.delete_file(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +FilesResources = (r'/files/(.*)', FilesHandler) +FileResource = (r'/file/(.*)', FileHandler) \ No newline at end of file diff --git a/virtshell_server/provisioning/files_repository.py b/virtshell_server/provisioning/files_repository.py new file mode 100644 index 0000000..177f528 --- /dev/null +++ b/virtshell_server/provisioning/files_repository.py @@ -0,0 +1,22 @@ +import provisioning +from storage import StorageFactory +import json + +class FilesRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('files') + + def create_file(self, file): + return self.storage_db.create(file) + + def get_all_files(self): + return self.storage_db.get() + + def get_file(self, name): + return self.storage_db.get("name", name) + + def update_file(self, name, file): + return self.storage_db.update("name", name, file) + + def delete_file(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/provisioning/images.py b/virtshell_server/provisioning/images.py new file mode 100644 index 0000000..f87795a --- /dev/null +++ b/virtshell_server/provisioning/images.py @@ -0,0 +1,22 @@ +from provisioning.images_repository import ImagesRepository +import uuid + +class Images(object): + def __init__(self): + self.images_repository = ImagesRepository() + + def get_all_images(self): + return self.images_repository.get_all_images() + + def get_image(self, name): + return self.images_repository.get_image(name) + + def create_image(self, image): + image['uuid'] = str(uuid.uuid4()) + return self.images_repository.create_image(image) + + def delete_image(self, name): + return self.images_repository.delete_image(name) + + def update_image(self, name, image): + return self.images_repository.update_image(name, image) \ No newline at end of file diff --git a/virtshell_server/provisioning/images_api.py b/virtshell_server/provisioning/images_api.py new file mode 100644 index 0000000..4849017 --- /dev/null +++ b/virtshell_server/provisioning/images_api.py @@ -0,0 +1,63 @@ +import tornado.ioloop +import tornado.web +from provisioning.images import Images +import json + +class ImagesHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.images = Images() + self.logger = logger + + def get(self, name=None): + self.logger.info("images GET " + name) + if name: + result = self.images.get_image(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.images.get_all_images() + if result['status'] == 'ok': + images = result['documents'] + response = {'images': images} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("images POST " + name) + image = tornado.escape.json_decode(self.request.body) + result = self.images.create_image(image) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("images PUT " + name) + if name: + image = tornado.escape.json_decode(self.request.body) + result = self.images.update_image(name, image) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("images DELETE " + name) + if name: + result = self.images.delete_image(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +ImagesResources = (r'/images/(.*)', ImagesHandler) \ No newline at end of file diff --git a/virtshell_server/provisioning/images_repository.py b/virtshell_server/provisioning/images_repository.py new file mode 100644 index 0000000..7d51cf6 --- /dev/null +++ b/virtshell_server/provisioning/images_repository.py @@ -0,0 +1,22 @@ +import provisioning +from storage import StorageFactory +import json + +class ImagesRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('images') + + def create_image(self, image): + return self.storage_db.create(image) + + def get_all_images(self): + return self.storage_db.get() + + def get_image(self, name): + return self.storage_db.get("name", name) + + def update_image(self, name, image): + return self.storage_db.update("name", name, image) + + def delete_image(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/provisioning/provisioners.py b/virtshell_server/provisioning/provisioners.py new file mode 100644 index 0000000..688500f --- /dev/null +++ b/virtshell_server/provisioning/provisioners.py @@ -0,0 +1,22 @@ +from provisioning.provisioners_repository import ProvisionersRepository +import uuid + +class Provisioners(object): + def __init__(self): + self.provisioners_repository = ProvisionersRepository() + + def get_all_provisioners(self): + return self.provisioners_repository.get_all_provisioners() + + def get_provisioner(self, name): + return self.provisioners_repository.get_provisioner(name) + + def create_provisioner(self, provisioner): + provisioner['uuid'] = str(uuid.uuid4()) + return self.provisioners_repository.create_provisioner(provisioner) + + def delete_provisioner(self, name): + return self.provisioners_repository.delete_provisioner(name) + + def update_provisioner(self, name, provisioner): + return self.provisioners_repository.update_provisioner(name, provisioner) \ No newline at end of file diff --git a/virtshell_server/provisioning/provisioners_api.py b/virtshell_server/provisioning/provisioners_api.py new file mode 100644 index 0000000..39d37fc --- /dev/null +++ b/virtshell_server/provisioning/provisioners_api.py @@ -0,0 +1,63 @@ +import tornado.ioloop +import tornado.web +from provisioning.provisioners import Provisioners +import json + +class ProvisionersHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.provisioners = Provisioners() + self.logger = logger + + def get(self, name=None): + self.logger.info("provisioners GET " + name) + if name: + result = self.provisioners.get_provisioner(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.provisioners.get_all_provisioners() + if result['status'] == 'ok': + provisioners = result['documents'] + response = {'provisioners': provisioners} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("provisioners POST " + name) + provisioner = tornado.escape.json_decode(self.request.body) + result = self.provisioners.create_provisioner(provisioner) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("provisioners PUT " + name) + if name: + provisioner = tornado.escape.json_decode(self.request.body) + result = self.provisioners.update_provisioner(name, provisioner) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("provisioners DELETE " + name) + if name: + result = self.provisioners.delete_provisioner(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +ProvisionersResources = (r'/provisioners/(.*)', ProvisionersHandler) \ No newline at end of file diff --git a/virtshell_server/provisioning/provisioners_repository.py b/virtshell_server/provisioning/provisioners_repository.py new file mode 100644 index 0000000..3141e34 --- /dev/null +++ b/virtshell_server/provisioning/provisioners_repository.py @@ -0,0 +1,22 @@ +import provisioning +from storage import StorageFactory +import json + +class ProvisionersRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('provisioners') + + def create_provisioner(self, provisioner): + return self.storage_db.create(provisioner) + + def get_all_provisioners(self): + return self.storage_db.get() + + def get_provisioner(self, name): + return self.storage_db.get("name", name) + + def update_provisioner(self, name, provisioner): + return self.storage_db.update("name", name, provisioner) + + def delete_provisioner(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/security/__init__.py b/virtshell_server/security/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/virtshell_server/security/groups.py b/virtshell_server/security/groups.py new file mode 100644 index 0000000..ea04979 --- /dev/null +++ b/virtshell_server/security/groups.py @@ -0,0 +1,22 @@ +from security.groups_repository import GroupsRepository +import uuid + +class Groups(object): + def __init__(self): + self.groups_repository = GroupsRepository() + + def get_all_groups(self): + return self.groups_repository.get_all_groups() + + def get_group(self, name): + return self.groups_repository.get_group(name) + + def create_group(self, group): + group['uuid'] = str(uuid.uuid4()) + return self.groups_repository.create_group(group) + + def delete_group(self, name): + return self.groups_repository.delete_group(name) + + def update_group(self, name, group): + return self.groups_repository.update_group(name, group) \ No newline at end of file diff --git a/virtshell_server/security/groups_api.py b/virtshell_server/security/groups_api.py new file mode 100644 index 0000000..de632ce --- /dev/null +++ b/virtshell_server/security/groups_api.py @@ -0,0 +1,63 @@ +import tornado.ioloop +import tornado.web +from security.groups import Groups +import json + +class GroupsHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.groups = Groups() + self.logger = logger + + def get(self, name=None): + self.logger.info("groups GET " + name) + if name: + result = self.groups.get_group(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.groups.get_all_groups() + if result['status'] == 'ok': + groups = result['documents'] + response = {'groups': groups} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("groups POST " + name) + group = tornado.escape.json_decode(self.request.body) + result = self.groups.create_group(group) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("groups PUT " + name) + if name: + group = tornado.escape.json_decode(self.request.body) + result = self.groups.update_group(name, group) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("groups DELETE " + name) + if name: + result = self.groups.delete_group(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +GroupsResources = (r'/groups/(.*)', GroupsHandler) \ No newline at end of file diff --git a/virtshell_server/security/groups_repository.py b/virtshell_server/security/groups_repository.py new file mode 100644 index 0000000..116bee1 --- /dev/null +++ b/virtshell_server/security/groups_repository.py @@ -0,0 +1,22 @@ +import security +from storage import StorageFactory +import json + +class GroupsRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('groups') + + def create_group(self, group): + return self.storage_db.create(group) + + def get_all_groups(self): + return self.storage_db.get() + + def get_group(self, name): + return self.storage_db.get("name", name) + + def update_group(self, name, group): + return self.storage_db.update("name", name, group) + + def delete_group(self, name): + return self.storage_db.delete("name", name) \ No newline at end of file diff --git a/virtshell_server/security/users.py b/virtshell_server/security/users.py new file mode 100644 index 0000000..e62d8df --- /dev/null +++ b/virtshell_server/security/users.py @@ -0,0 +1,22 @@ +from security.users_repository import UsersRepository +import uuid + +class Users(object): + def __init__(self): + self.users_repository = UsersRepository() + + def get_all_users(self): + return self.users_repository.get_all_users() + + def get_user(self, name): + return self.users_repository.get_user(name) + + def create_user(self, user): + user['uuid'] = str(uuid.uuid4()) + return self.users_repository.create_user(user) + + def delete_user(self, name): + return self.users_repository.delete_user(name) + + def update_user(self, name, user): + return self.users_repository.update_user(name, user) \ No newline at end of file diff --git a/virtshell_server/security/users_api.py b/virtshell_server/security/users_api.py new file mode 100644 index 0000000..c4937e9 --- /dev/null +++ b/virtshell_server/security/users_api.py @@ -0,0 +1,63 @@ +import tornado.ioloop +import tornado.web +from security.users import Users +import json + +class UsersHandler(tornado.web.RequestHandler): + def initialize(self, logger): + self.users = Users() + self.logger = logger + + def get(self, name=None): + self.logger.info("users GET " + name) + if name: + result = self.users.get_user(name) + if result['status'] == 'ok': + response = result['document'] + else: + response = {'error': result['reason']} + else: + result = self.users.get_all_users() + if result['status'] == 'ok': + users = result['documents'] + response = {'users': users} + else: + response = {'error': result['reason']} + return self.write(json.dumps(response)) + + def post(self, name=None): + self.logger.info("users POST " + name) + user = tornado.escape.json_decode(self.request.body) + result = self.users.create_user(user) + if result['status'] == 'ok': + response = {"create": "success"} + else: + response = {"create": "error", "reason": result['reason']} + return self.write(json.dumps(response)) + + def put(self, name=None): + self.logger.info("users PUT " + name) + if name: + user = tornado.escape.json_decode(self.request.body) + result = self.users.update_user(name, user) + if result['status'] == 'ok': + response = {"update": "success"} + else: + response = {"update": "error", "reason": result['reason']} + else: + response = {"update": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + + def delete(self, name=None): + self.logger.info("users DELETE " + name) + if name: + result = self.users.delete_user(name) + if result['status'] == 'ok': + response = {"delete": "success"} + else: + response = {"delete": "error", "reason": result['reason']} + else: + response = {"delete": "error", "reason": "missing name parameter"} + return self.write(json.dumps(response)) + +UsersResources = (r'/users/(.*)', UsersHandler) \ No newline at end of file diff --git a/virtshell_server/security/users_repository.py b/virtshell_server/security/users_repository.py new file mode 100644 index 0000000..0db336f --- /dev/null +++ b/virtshell_server/security/users_repository.py @@ -0,0 +1,22 @@ +import security +from storage import StorageFactory +import json + +class UsersRepository(object): + def __init__(self): + self.storage_db = StorageFactory.get_storage('users') + + def create_user(self, user): + return self.storage_db.create(user) + + def get_all_users(self): + return self.storage_db.get() + + def get_user(self, name): + return self.storage_db.get("username", name) + + def update_user(self, name, user): + return self.storage_db.update("username", name, user) + + def delete_user(self, name): + return self.storage_db.delete("username", name) \ No newline at end of file diff --git a/virtshell_server/server.py b/virtshell_server/server.py new file mode 100644 index 0000000..233c818 --- /dev/null +++ b/virtshell_server/server.py @@ -0,0 +1,60 @@ +import os +import logger +import config +import tornado.web +import tornado.ioloop +from security.users_api import UsersResources +from managment.tasks_api import TasksResources +from managment.hosts_api import HostsResources +from security.groups_api import GroupsResources +from managment.tasks_api import StatusTasksResources +from managment.instances_api import InstancesResources +from managment.partitions_api import PartitionsResources +from managment.enviroments_api import EnviromentsResources +from managment.partitions_api import PartitionsHostResources +from provisioning.provisioners_api import ProvisionersResources +from provisioning.images_api import ImagesResources +from provisioning.files_api import FilesResources +from provisioning.files_api import FileResource + +def get_handlers(log): + handlers_without_arguments = [HostsResources, + UsersResources, + GroupsResources, + ProvisionersResources, + InstancesResources, + EnviromentsResources, + PartitionsResources, + PartitionsHostResources, + TasksResources, + StatusTasksResources, + ImagesResources, + FilesResources, + FileResource] + + logger_argument = (dict(logger=log), ) + handlers_with_arguments = [] + for handler in handlers_without_arguments: + handler += logger_argument + handlers_with_arguments.append(handler) + + return handlers_with_arguments + +def main(debug = True, port = config.PORT): + try: + log = logger.get_logger('virtshell_server') + log.info("server started...") + + handlers = get_handlers(log) + + application = tornado.web.Application(handlers, + static_path = config.PATH_STATIC_FILES, + debug = debug, + autoreload = debug) + application.listen(port) + tornado.ioloop.IOLoop.current().start() + except KeyboardInterrupt: + log.info("server stoped...") + +if __name__ == '__main__': + main(debug = True) diff --git a/virtshell_server/storage/__init__.py b/virtshell_server/storage/__init__.py new file mode 100644 index 0000000..41d96ac --- /dev/null +++ b/virtshell_server/storage/__init__.py @@ -0,0 +1,8 @@ +from storage.mongodb import MongoDB +from config import DATABASE_PROVIDER + +class StorageFactory(object): + @staticmethod + def get_storage(database_name): + if DATABASE_PROVIDER == 'mongodb': + return MongoDB(database_name) \ No newline at end of file diff --git a/virtshell_server/storage/mongodb.py b/virtshell_server/storage/mongodb.py new file mode 100644 index 0000000..7792427 --- /dev/null +++ b/virtshell_server/storage/mongodb.py @@ -0,0 +1,93 @@ +import storage +from config import MONGODB +from config import DATABASE_NAME +from pymongo import MongoClient + +class MongoDB(object): + """ CRUD operations on any collection in MongoDB """ + + def __init__(self, collection_name): + client = MongoClient(MONGODB) + self.mongodb = client[DATABASE_NAME] + self.collection_name = collection_name + self.collection = self._get_collection() + + def _get_collection(self): + return self.mongodb[self.collection_name] + + def get(self, key=None, value=None): + try: + if key is None and value is None: + documents = [] + for document in self.collection.find(): + del document['_id'] + documents.append(document) + return {"status": "ok", "documents": documents} + else: + document = self.collection.find_one({key: value}) + if document != None: + del document["_id"] + return {"status": "ok", "document": document} + else: + return {"status": "error", "reason": "document not found"} + except Exception as e: + return {"status": "error", "reason": e} + + def create(self, document): + try: + if document is not None: + document_id = self.collection.insert_one(document).inserted_id + return {"status": "ok"} + else: + return {"status": "error", "reason": "document is None"} + except Exception as e: + return {"status": "error", "reason": e} + + def update(self, key, value, document): + try: + if document is not None: + result = self.collection.find_one_and_update({key: value}, + {'$set': document}) + return {"status": "ok"} + else: + return {"status": "error", "reason": "document is None"} + except Exception as e: + return {"status": "error", "reason": e} + + def delete(self, key, value): + try: + result = self.collection.delete_one({key: value}) + return {"status": "ok"} + except Exception as e: + return {"status": "error", "reason": e} + + def delete_all(self): + try: + result = self.collection.delete_many({}) + return {"status": "ok"} + except Exception as e: + return {"status": "error", "reason": e} + + def get_property(self, property_name, instance, enviroment): + try: + data = self.collection.find_one({"instance": instance, + "enviroment": enviroment}) + if data != None: + if property_name in data: + return {"status": "ok", "property": data[property_name]} + else: + return {"status": "error", "reason": "property not found"} + else: + return {"status": "error", "reason": "instance or enviroment not found"} + except Exception as e: + return {"status": "error", "reason": e} + + def get_all_tasks_by_status(self, status_name): + try: + documents = [] + for document in self.collection.find({'status':status_name}): + del document['_id'] + documents.append(document) + return {"status": "ok", "documents": documents} + except Exception as e: + return {"status": "error", "reason": e} \ No newline at end of file