Compare commits

..

15 Commits

Author SHA1 Message Date
Brendan LE GLAUNEC d98d78cd73 v1.1.3 : Travis functional testing & minor changes 2016-11-23 08:49:19 +01:00
Brendan LE GLAUNEC e9ffb44b45 v1.1.2 : Travis build test integration & changes to docker deployment 2016-11-23 08:31:35 +01:00
Brendan LE GLAUNEC 9f85415d89 Added docker pulls badge to README.md 2016-11-14 13:40:54 +01:00
Brendan LE GLAUNEC c0d890acad Merge pull request #20 from EtixLabs/bugfix/version-1.1.1
v1.1.1 : Fixed functional tests & Multiple bugfixes
2016-11-12 13:13:14 +01:00
Brendan LE GLAUNEC 553524ae43 v1.1.1 : Fixed functional tests & Multiple bugfixes 2016-11-12 12:55:32 +01:00
Brendan LE GLAUNEC f80af6bd58 Merge pull request #18 from EtixLabs/bugfix/remove-unnecessary-null-pointer-checks
v1.1.0 : Removed unnecessary null pointer checks
2016-11-03 16:36:46 +01:00
Brendan LE GLAUNEC 6c127e4cbe v1.1.0 : Removed unnecessary null pointer checks 2016-11-03 16:34:54 +01:00
Brendan LE GLAUNEC c16c8c0aaa v1.1.0 : Docker Hub Readme changes 2016-11-03 08:45:20 +01:00
Brendan LE GLAUNEC be74c3c814 v1.1.0 : Deployment updated & Docker Hub 2016-11-02 14:42:33 +01:00
Brendan LE GLAUNEC 2f93ddd7e5 v1.1.0 : Updated deployment 2016-11-02 14:42:22 +01:00
Brendan LE GLAUNEC 108f869a43 v1.1.0 : Updated package 2016-11-02 08:42:45 +01:00
Brendan LE GLAUNEC 6685f74a90 v1.1.0 : Fixed multithreading & added timeout to ffmpeg 2016-11-02 07:56:03 +01:00
Brendan LE GLAUNEC 37176292d0 Merge branch 'master' of github.com:EtixLabs/cameradar 2016-10-31 10:13:52 +01:00
Brendan LE GLAUNEC a49b8ef481 v1.1.0 : Updated package 2016-10-31 10:02:53 +01:00
Brendan LE GLAUNEC 8e26751247 v1.1.0 : Added GST RTSP standard in cmd line 2016-10-31 10:01:13 +01:00
56 changed files with 1070 additions and 609 deletions
+5 -1
View File
@@ -40,4 +40,8 @@ build/
# Deps
deps/boost/
deps/jsoncpp/
mysql-connector/
mysql-connector/
# Test
test/cameradartest.conf.json
test/cameradar_*_Debug_Linux.tar.gz
+58
View File
@@ -0,0 +1,58 @@
language: generic
sudo: required
dist: trusty
before_install:
- echo "Testing Docker Hub credentials"
- docker login -u=$DOCKER_USERNAME -p=$DOCKER_PASSWORD
- echo "Docker Hub credentials are working"
- sudo apt-get update -qq
- sudo apt-get install -y software-properties-common
- sudo add-apt-repository -y ppa:mc3man/trusty-media
- sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
- sudo apt-get update -qq
- sudo apt-get install -y nmap
- sudo apt-get install -y ffmpeg
- sudo apt-get install -y cmake
- sudo apt-get install -y libboost-all-dev
- sudo apt-get install -y libgstreamer1.0-dev
- sudo apt-get install -y gstreamer1.0-plugins-base
- sudo apt-get install -y gstreamer1.0-plugins-good
- sudo apt-get install -y libcurl4-openssl-dev
- sudo apt-get install -y libmysqlclient18
- sudo apt-get install -y mysql-client
install:
- export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
- export PACKAGE_NAME="cameradar_*_Debug_Linux"
matrix:
include:
- os: linux
env: TEST_TYPE='BUILD' WORKDIR='deployment' COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 CMAKE_CXX_COMPILER=g++-5
addons:
apt:
packages:
- g++-5
sources: &sources
- ubuntu-toolchain-r-test
- os: linux
env: TEST_TYPE='TEST' WORKDIR='test' COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 CMAKE_CXX_COMPILER=g++-5
addons:
apt:
packages:
- g++-5
sources: &sources
- ubuntu-toolchain-r-test
script:
- cd ${WORKDIR}
- ./build_last_package.sh Debug
- tar xvf ${PACKAGE_NAME}.tar.gz
- find ${DEPS_DIR} -name "*.so*" -exec cp {} ${PACKAGE_NAME}/libraries \;
- tar -czvf ${PACKAGE_NAME}.tar.gz ${PACKAGE_NAME}
- if [[ "$TEST_TYPE" == "BUILD" ]]; then docker build -t cameradar . && docker run -v /tmp/thumbs:/tmp/thumbs cameradar; else ./test.sh ; fi
after_success:
- echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)"
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then echo -e "Push Container to Docker Hub" && docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD && docker tag cameradar $DOCKER_REPO:latest && docker push $DOCKER_REPO; fi
+54
View File
@@ -2,6 +2,60 @@
This file lists all versions of the repository and precises all changes.
## v1.1.3
#### Minor changes :
* Added automatic pushes to DockerHub to the travis integration
* Made travis configuration file better
* Changed the package generation scripts to make them report errors
* Removed old etix_rtsp_server binary from the test folder
#### Bugfixes :
* Fixed an issue that made it mandatory to launch tests at least once so that they can work the second time
* Fixed an issue that made the golang testing tool not compile in the testing script
* Fixed an issue that made the golang testing tool sometimes ignore some tests
* The previous known issue has been investigated and we don't know where it came from. However after a night of testing I have been unable to reproduce it, so I will consider it closed
## v1.1.2
#### Minor changes :
* Added travis integration
* Added default environment value for Docker deployment
* Updated docker image description with new easy usage
* Updated README badges style (replaced flat with square-flat)
* Build last package can now also generate a debug package if given the `Debug` command-line argument
#### Known issues :
* There is still the issue with Camera Emulation Server, see the [previous version's patchnote](#v1.1.1) for more information.
## v1.1.1
#### Minor changes :
* Removed unnecessary null pointer checks (thanks to https://github.com/elfring)
* Updated package description
* Removed debug message in CMake build
* Added `/ch01.264` to the URL dictionary in the deployment (Comelit default RTSP URL)
* Updated tests partially (still needs work to make the code cleaner)
* Variable names are now compliant with Golang best practices
* JSON variable names are back to normal
* Functions have been moved in more appropriate source files
* Structure definitions have been moved in more appropriate source files
* Source files have been renamed to be more relevant
* JUnit output now considers each camera as a test case
* JUnit output now contains errors which makes debugging much easier
* Added header files where it was forgotten
#### Bugfixes :
* Fixed an issue where if you loose your internet connection during thumbnail generation, FFMpeg would get stuck forever and thus Cameradar would never finish
* Fixed an issue where multithreading could cause crashes
* Fixed an issue where the routes dictionary was mistaken for the credentials dictionary
* Fixed issues with the golang testing tool
* Fixed automated camera generation
* Fixed docker IP address resolution
#### Known issues :
* There is an issue with Camera Emulation Server that makes it impossible for Cameradar to generate thumbnails, which is why right now the verification of the thumbnails presence is commented and it is assumed correct. It is probably an issue with GST-RTSP-Server but requires investigation.
## v1.1.0
#### Major changes :
+5 -8
View File
@@ -19,11 +19,9 @@ set (PROJECT_NAME cameradar)
project (${PROJECT_NAME})
message ("Here")
set (${PROJECT_NAME}_VERSION_MAJOR 1)
set (${PROJECT_NAME}_VERSION_MINOR 1)
set (${PROJECT_NAME}_VERSION_PATCH 0)
set (${PROJECT_NAME}_VERSION_PATCH 3)
set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}")
find_package(Git REQUIRED)
@@ -34,7 +32,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wextra -Wno-unused-function")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") #enable error coloration on gcc
# release specific flags
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2") #enable error coloration on gcc
set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
#debug specific flags
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
@@ -102,9 +100,8 @@ include_directories (
set (${CAMERADAR_BINARIES} "")
set (${CAMERADAR_LIBRARIES} "")
#build cache managers
# Build cache managers
add_subdirectory (deps)
message ("Debug")
add_subdirectory (cameradar_standalone)
add_subdirectory (cache_managers)
@@ -115,11 +112,11 @@ install (FILES ${CAMERADAR_CACHE_MANAGERS} DESTINATION cache_managers)
install (FILES ${CAMERADAR_LIBRARIES} DESTINATION libraries)
install (DIRECTORY ${CMAKE_SOURCE_DIR}/deps/licenses DESTINATION libraries)
# cpack configuration
# CPack configuration
include (InstallRequiredSystemLibraries)
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar")
set (CPACK_PACKAGE_VENDOR "Etix Labs")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar tool")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cameradar hacks its way into RTSP CCTV cameras")
set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${${PROJECT_NAME}_VERSION}_${CMAKE_BUILD_TYPE}_${CMAKE_SYSTEM_NAME}")
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+52 -4
View File
@@ -2,8 +2,10 @@
## An RTSP surveillance camera access multitool
[![cameradar License](https://img.shields.io/badge/license-Apache-blue.svg)](#license)
[![Latest release](https://img.shields.io/badge/release-1.1.0-green.svg)](https://github.com/EtixLabs/cameradar/releases/latest)
[![cameradar License](https://img.shields.io/badge/license-Apache-blue.svg?style=flat-square)](#license)
[![Docker Pulls](https://img.shields.io/docker/pulls/ullaakut/cameradar.svg?style=flat-square)](https://hub.docker.com/r/ullaakut/cameradar/)
[![Build](https://img.shields.io/travis/EtixLabs/cameradar/master.svg?style=flat-square)](https://travis-ci.org/EtixLabs/cameradar)
[![Latest release](https://img.shields.io/badge/release-1.1.3-green.svg?style=flat-square)](https://github.com/EtixLabs/cameradar/releases/latest)
#### Cameradar allows you to:
@@ -24,6 +26,7 @@ Of course, you can also call for individual tasks if you plug in a Database to C
## Table of content
- [Docker Image](#docker-image)
- [Quick install](#quick-install)
- [Dependencies](#quick-install###dependencies)
- [Five steps guide](#quick-install###five-steps-guide)
@@ -40,8 +43,31 @@ Of course, you can also call for individual tasks if you plug in a Database to C
- [Under the hood](#under-the-hood)
- [Contribution](#contribution)
- [Next improvements](#next-improvements)
- [Frequently Asked Questions](#frequently-asked-questions)
- [License](#license)
## Docker Image
This is the fastest and simplest way to use Cameradar. To do this you will just need `docker` on your machine.
Run
```
docker run \
-v /tmp/thumbs/:/tmp/thumbs \
-e CAMERAS_SUBNETWORKS=your_subnetwork \
ullaakut/cameradar:tag
```
* `your_subnetwork` can be a subnet (e.g.: `172.16.100.0/24`) or even an IP (e.g.: `172.16.100.10`).
* `tag` allows you to specify a specific version for camerada. If you don't specify any tag, you will use the latest version by default (recommended)
Check [Cameradar's readme on the Docker Hub](https://hub.docker.com/r/ullaakut/cameradar/) for more information and more command-line options.
The generated thumbnails will be in `/tmp/thumbs` on both your machine and the `cameradar` container.
For more complex use of the Docker image, see the `Environment variables` part of [Cameradar's readme on the Docker Hub](https://hub.docker.com/r/ullaakut/cameradar/).
## Quick install
The quick install uses docker to build Cameradar without polluting your machine with dependencies and makes it easy to deploy Cameradar in a few commands. **However, it may require networking knowledge, as your docker containers will need access to the cameras subnetwork.**
@@ -55,11 +81,14 @@ The only dependencies are `docker`, `docker-tools`, `git` and `make`.
1. `git clone https://github.com/EtixLabs/cameradar.git`
2. Go into the Cameradar repository, then to the `deployment` directory
3. Tweak the `conf/cameradar.conf.json` as you need (see [the onfiguration guide here](#configuration) for more information)
4. Run `docker-compose build cameradar` to build the cameradar container
5. Run `docker-compose up cameradar` to launch Cameradar
4. Run `docker-compose build & docker-compose up`
By default, the version of the package in the deployment should be the last stable release.
If you want to scan a different subnetwork or different ports, change the values `CAMERAS_SUBNETWORKS` and `CAMERAS_PORTS` in the `docker-compose.yml` file.
The generated thumbnails will be in the `cameradar_thumbnails` folder after cameradar has finished executing.
If you want to deploy your custom version of Cameradar using the same method, you should check the [advanced docker deployment](#advanced-docker-deployment) tutorial here.
## Manual installation
@@ -222,6 +251,7 @@ If you're still in your console however, you can go even faster by using **vlc i
* Needs either to be launched with the -d option or to use an advanced cache manager (DB, file, ...) with data already present
* **"-v"** : Display Cameradar's version
* **"-h"** : Display this help
* **"--gst-rtsp-server"** : Use this option if the bruteforce does not seem to work (only detects the username but not the path, or the opposite). This option will switch the order of the bruteforce to prioritize path over credentials, which is the way priority is handled for cameras that use GStreamer's RTSP server.
## Under the hood
@@ -261,6 +291,24 @@ If you have other cool ideas, feel free to share them with me at [brendan.leglau
- [ ] Make a standalone docker image
- [ ] Push to DockerHub
## Frequently Asked Questions
> My camera's credentials are guessed by Cameradar but the RTSP url is not!
Your camera probably uses GST RTSP Server internally. Try the `--gst-rtsp-server` command-line option, and if it does not work, send me the cameradar output in DEBUG mode (`-l 1`) and I will help you.
> Cameradar does not detect any camera!
That means that either your cameras are not streaming in RTSP or that they are not on the subnetwork you are scanning. In most cases, CCTV cameras will be on a private subnetwork. Use the `-s` option to specify your camera's subnetwork.
> Cameradar detects my cameras, but does not manage to access them at all!
Maybe your cameras have been configured and the credentials / URL have been changed. Cameradar only guesses using default constructor values. However, you can use your own dictionary in which you just have to add your passwords. To do that, see how the [configuration](#configuration) works.
> It does not compile
You probably missed the part with the dependencies! Use the quick docker deployment, it will be easier and will not pollute your machine with useless dependencies! `;)`
## License
Copyright 2016 Etix Labs
@@ -81,7 +81,7 @@ db_connection::execute(const std::string& request) {
return_value = { execute_result::no_row_updated, "No row updated" };
}
} catch (sql::SQLException& e) { return_value = { execute_result::sql_error, e.what() }; }
if (stmt) { delete stmt; }
delete stmt;
return return_value;
}
@@ -103,8 +103,7 @@ db_connection::query(const std::string& query) {
} catch (sql::SQLException& e) {
return_value = { nullptr, execute_result::sql_error, e.what() };
}
if (stmt) { delete stmt; }
delete stmt;
return return_value;
}
+1 -1
View File
@@ -45,7 +45,7 @@ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
endif()
include (find_sources)
find_sources ("src" "include" "src/models" "src/repositories" "src/tasks")
find_sources ("src" "include" "src/tasks")
add_executable (cameradar ${SOURCES})
target_link_libraries (cameradar pthread jsoncpp dl curl ${GSTREAMER_LIBRARIES})
+7 -16
View File
@@ -1,25 +1,16 @@
{
"mysql_db" : {
"host" : "0.0.0.0",
"host" : "cameradar-database",
"port" : 3306,
"user": "root",
"password": "root",
"db_name": "cctv_dev"
"db_name": "cmrdr"
},
"subnets" : "172.16.100.11",
// If not specified, will scan all ports (1-65535)
"subnets" : "localhost",
"ports" : "554,8554",
"rtsp_url_file" : "conf/url.json",
"rtsp_ids_file" : "conf/ids.json",
// You must give an accessible path to an already existing directory
"thumbnail_storage_path" : "/tmp",
// This is the path that will be used in the Docker container
// if you're not familiar with Docker, only change the
// cache_manager_name value
"cache_manager_path" : "../cache_managers",
"rtsp_url_file" : "/cameradar/conf/url.json",
"rtsp_ids_file" : "/cameradar/conf/ids.json",
"thumbnail_storage_path" : "/tmp/thumbs",
"cache_manager_path" : "/cameradar/cache_managers",
"cache_manager_name" : "dumb"
}
+18 -9
View File
@@ -13,10 +13,10 @@
// limitations under the License.
#include "cachemanager.h" // for cache_manager
#include <algorithm> // for move
#include <dlfcn.h> // for dlerror, dlclose, dlopen, dlsym, etc
#include <logger.h> // for LOG_ERR_
#include <stdbool.h> // for bool, false, true
#include <algorithm> // for move
#include <errno.h>
@@ -46,10 +46,7 @@ cache_manager::cache_manager(cache_manager&& old)
}
cache_manager::~cache_manager() {
if (this->ptr) {
delete this->ptr;
this->ptr = nullptr;
}
delete this->ptr;
if (this->handle) { dlclose(handle); }
}
@@ -105,13 +102,25 @@ cache_manager_iface* cache_manager::operator->() { return this->ptr; }
const cache_manager_iface* cache_manager::operator->() const { return this->ptr; }
bool operator==(std::nullptr_t nullp, const cache_manager& p) { return p.ptr == nullp; }
bool
operator==(std::nullptr_t nullp, const cache_manager& p) {
return p.ptr == nullp;
}
bool operator==(const cache_manager& p, std::nullptr_t nullp) { return p.ptr == nullp; }
bool
operator==(const cache_manager& p, std::nullptr_t nullp) {
return p.ptr == nullp;
}
bool operator!=(std::nullptr_t nullp, const cache_manager& p) { return p.ptr != nullp; }
bool
operator!=(std::nullptr_t nullp, const cache_manager& p) {
return p.ptr != nullp;
}
bool operator!=(const cache_manager& p, std::nullptr_t nullp) { return p.ptr != nullp; }
bool
operator!=(const cache_manager& p, std::nullptr_t nullp) {
return p.ptr != nullp;
}
cache_manager_base&
cache_manager_base::get_instance() {
+2 -2
View File
@@ -92,12 +92,12 @@ bool
configuration::load_url() {
std::string content;
LOG_DEBUG_("Trying to open ids file from " + this->rtsp_ids_file, "configuration");
LOG_DEBUG_("Trying to open url file from " + this->rtsp_url_file, "configuration");
if (this->rtsp_url_file.size()) {
content = read_file(this->rtsp_url_file.c_str()).second;
} else {
LOG_WARN_(
"No ids file detected in your configuration, Cameradar will use "
"No url file detected in your configuration, Cameradar will use "
"the default one "
"instead.",
"configuration");
+13 -2
View File
@@ -13,17 +13,21 @@
// limitations under the License.
#include <describe.h>
#include <mutex>
namespace etix {
namespace cameradar {
std::mutex m;
// Ugly workaround
size_t
write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
// I'm sorry for this
// Forget you ever saw it
(void)buffer;
(void)userp;
if (not buffer || not size || not nmemb) return 0;
return size * nmemb;
}
@@ -37,6 +41,9 @@ curl_describe(const std::string& path, bool logs) {
struct curl_slist* custom_msg = NULL;
char URL[256];
long rc;
m.lock();
curl_global_init(0);
m.unlock();
csession = curl_easy_init();
if (csession == NULL) return -1;
sprintf(URL, "%s", path.c_str());
@@ -78,7 +85,11 @@ curl_describe(const std::string& path, bool logs) {
}
curl_easy_cleanup(csession);
LOG_DEBUG_("Response code : " + std::to_string(rc), "describe");
m.lock();
curl_global_cleanup();
m.unlock();
LOG_DEBUG_("[" + path + "] Response code : " + std::to_string(rc), "describe");
if (logs) {
// Some cameras return 400 instead of 401, don't know why.
// Some cameras timeout and then curl considers the status as 0
+16 -6
View File
@@ -17,7 +17,7 @@
namespace etix {
namespace cameradar {
using namespace std::chrono_literals;
using namespace std::chrono_literals;
// The main loop of the binary
void
@@ -49,7 +49,7 @@ dispatcher::run() {
// Waiting for task to cleanup / force stop command
while ((signal_handler::instance().should_stop() not_eq stop_priority::force_stop) and
doing_stuff()) {
std::this_thread::sleep_for(30ms);
std::this_thread::sleep_for(30ms);
}
worker.join();
}
@@ -63,8 +63,13 @@ dispatcher::do_stuff() {
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
}
if (opts.second.exist("-b")) {
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
if (opts.second.exist("--gst-rtsp-server")) {
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
} else {
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
}
}
if (opts.second.exist("-t")) {
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
@@ -76,8 +81,13 @@ dispatcher::do_stuff() {
!opts.second.exist("-g")) {
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
if (opts.second.exist("--gst-rtsp-server")) {
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
} else {
queue.push_back(new etix::cameradar::brutelogs(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::brutepath(cache, conf, nmap_output));
}
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
}
+7 -2
View File
@@ -41,6 +41,12 @@ parse_cmdline(int argc, char* argv[]) {
opt_parse.optional("-g", "Check if the stream can be opened with GStreamer", false);
opt_parse.optional("-v", "Display Cameradar's version", false);
opt_parse.optional("-h", "Display this help", false);
opt_parse.optional(
"--gst-rtsp-server",
"Change the order of the bruteforce to match GST RTSP Server's implementation of "
"RTSP. Some cameras and RTSP servers will use this standard instead of the more "
"standard one. For more information, see the README.md file.",
false);
opt_parse.execute();
if (opt_parse.exist("-h")) {
@@ -82,14 +88,13 @@ check_storage_path(const std::string& thumbnail_storage_path) {
int
main(int argc, char* argv[]) {
etix::tool::logger::get_instance("cameradar");
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::DEBUG);
auto args = parse_cmdline(argc, argv);
if (not args.first) return EXIT_FAILURE;
print_version();
if (not args.second.exist("-l")) {
etix::tool::logger::get_instance("cameradar").set_level(etix::tool::loglevel::DEBUG);
LOG_INFO_("No log level set, using log level 1", "main");
} else {
try {
+12 -5
View File
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rtsp_path.h>
#include <logger.h>
#include <rtsp_path.h>
namespace etix {
@@ -21,10 +21,17 @@ namespace cameradar {
const std::string
make_path(const stream_model& model) {
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
model.address + ":" + std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug");
return ret;
if (model.password != "" || model.username != "") {
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
model.address + ":" + std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug");
return ret;
} else {
std::string ret(model.service_name + "://" + model.address + ":" +
std::to_string(model.port) + model.route);
LOG_DEBUG_(ret, "debug");
return ret;
}
}
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
bool found = false;
std::string path = stream.service_name + "://";
if (username != "" || password != "") { path += username + ":" + password + "@"; }
path += stream.address + ":" + std::to_string(stream.port);
path += stream.address + ":" + std::to_string(stream.port) + stream.route;
LOG_INFO_("Testing ids : " + path, "brutelogs");
try {
if (curl_describe(path, true)) {
+1 -1
View File
@@ -54,7 +54,7 @@ parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) c
stream.service_name = "closed";
stream.product = "closed";
}
data.push_back(stream);
if (!stream.state.compare("open")) data.push_back(stream);
}
}
+1 -1
View File
@@ -24,7 +24,7 @@ print::run() const {
std::vector<stream_model> results = (*cache)->get_valid_streams();
std::ofstream file;
bool first = true;
file.open("result.json");
file.open("/tmp/shared/result.json");
file << "[\n";
for (const auto& stream : results) {
LOG_INFO_("Found a valid RTSP Stream and generated a thumbnail at : " +
@@ -48,6 +48,7 @@ thumbnail::generate_thumbnail(const stream_model& stream) const {
return false;
std::string ffmpeg_cmd =
"mkdir -p %s ; "
"timeout 20 "
"ffmpeg "
"-rtsp_transport tcp "
"-y "
+1 -2
View File
@@ -42,7 +42,6 @@ set (JSONCPP_INCLUDE_DIR "${SOURCE_DIR}/include" PARENT_SCOPE)
set (JSONCPP_LIBRARY_DIR "${SOURCE_DIR}/src/lib_json")
set (JSONCPP_LIBRARY_DIR ${JSONCPP_LIBRARY_DIR} PARENT_SCOPE)
file(GLOB JSONCPP_INSTALL_DEPENDENCIES "${JSONCPP_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}jsoncpp*${CMAKE_SHARED_LIBRARY_SUFFIX}*")
file(GLOB JSONCPP_INSTALL_DEPENDENCIES "${JSONCPP_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}jsoncpp${CMAKE_SHARED_LIBRARY_SUFFIX}*")
list (APPEND CAMERADAR_INSTALL_DEPENDENCIES ${JSONCPP_INSTALL_DEPENDENCIES})
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
+15 -8
View File
@@ -1,8 +1,16 @@
# Copyright (C) 2015 Etix Labs - All Rights Reserved.
# All information contained herein is, and remains the property of Etix Labs and its suppliers,
# if any. The intellectual and technical concepts contained herein are proprietary to Etix Labs
# Dissemination of this information or reproduction of this material is strictly forbidden unless
# prior written permission is obtained from Etix Labs.
## Copyright 2016 Etix Labs
##
## 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.
# MySQL Connector dependency
message(STATUS "Configuring deps.mysqlconnector")
@@ -36,8 +44,7 @@ set (MYSQL_CONNECTOR_LIBRARY_DIR "${MYSQL_CONNECTOR_PATH}/lib")
set (MYSQL_CONNECTOR_LIBRARY_DIR ${MYSQL_CONNECTOR_LIBRARY_DIR} PARENT_SCOPE)
# list all the hiredis libraries
file(GLOB MYSQL_CONNECTOR_INSTALL_DEPENDENCIES "${MYSQL_CONNECTOR_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlcppconn*${CMAKE_SHARED_LIBRARY_SUFFIX}*")
# list all the mysql libraries
file(GLOB MYSQL_CONNECTOR_INSTALL_DEPENDENCIES "${MYSQL_CONNECTOR_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlcppconn${CMAKE_SHARED_LIBRARY_SUFFIX}*")
list (APPEND CAMERADAR_INSTALL_DEPENDENCIES ${MYSQL_CONNECTOR_INSTALL_DEPENDENCIES})
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
+6 -3
View File
@@ -18,7 +18,10 @@ RUN apt-get update && apt-get install -y \
ADD cameradar_*_Release_Linux.tar.gz /
RUN mv cameradar_*_Release_Linux cameradar
RUN mkdir /conf
ADD run.sh /run.sh
COPY conf /cameradar/conf
CMD ["/run.sh"]
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s /usr/local/bin/docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/cameradar/bin/cameradar", "-c", "/cameradar/conf/cameradar.conf.json"]
+45 -11
View File
@@ -5,17 +5,51 @@ COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
COL_BLUE=$ESC_SEQ"34;01m"
COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m"
echo -e $COL_YELLOW"Deleting old package ... "$COL_RESET
rm -f cameradar_*_${1:-"Release"}_Linux.tar.gz
echo -e $COL_GREEN"OK!"$COL_RESET
echo -e $COL_YELLOW"Creating package ... "$COL_RESET
{ cd ..
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make package
cp cameradar_*_Release_Linux.tar.gz ../deployment
cd ../deployment
} &> /dev/null
cd ..
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
mkdir build
cd build
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
rm -f cameradar_*_${1:-"Release"}_Linux.tar.gz
cmake .. -DCMAKE_BUILD_TYPE=${1:-"Release"}
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
make package
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
cp cameradar_*_${1:-"Release"}_Linux.tar.gz ../deployment
cd ../deployment
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
echo -e $COL_GREEN"OK!"$COL_RESET
Binary file not shown.
Binary file not shown.
-25
View File
@@ -1,25 +0,0 @@
{
"mysql_db" : {
"host" : "__MYSQL_ADDR__",
"port" : __MYSQL_PORT__,
"user": "root",
"password": "root",
"db_name": "cmrdr"
},
"subnets" : "localhost",
// If not specified, will scan all ports (1-65535)
"ports" : "554,8554",
"rtsp_url_file" : "conf/url.json",
"rtsp_ids_file" : "conf/ids.json",
// You must give an accessible path to an already existing directory
"thumbnail_storage_path" : "/tmp",
// This is the path that will be used in the Docker container
// if you're not familiar with Docker, only change the
// cache_manager_name value
"cache_manager_path" : "/cameradar/cache_managers",
"cache_manager_name" : "mysql"
}
+16
View File
@@ -0,0 +1,16 @@
{
"mysql_db" : {
"host" : "cameradar-database",
"port" : 3306,
"user": "root",
"password": "$MYSQL_ROOT_PASSWORD",
"db_name": "cmrdr"
},
"subnets" : "$CAMERAS_SUBNETWORKS",
"ports" : "$CAMERAS_PORTS",
"rtsp_url_file" : "/cameradar/conf/url.json",
"rtsp_ids_file" : "/cameradar/conf/ids.json",
"thumbnail_storage_path" : "/tmp/thumbs",
"cache_manager_path" : "/cameradar/cache_managers",
"cache_manager_name" : "$CACHE_MANAGER"
}
+1
View File
@@ -24,6 +24,7 @@
"/camera.stm",
"/ch0",
"/ch001.sdp",
"/ch01.264",
"/ch0_unicast_firststream",
"/ch0_unicast_secondstream",
"/channel1",
+25 -16
View File
@@ -1,16 +1,25 @@
cameradar:
build: .
dockerfile: Dockerfile
env_file: env_file
volumes:
- "./conf:/tmp/conf:ro"
- "./cameradar_thumbnails:/tmp/cameradar_thumbnails"
links:
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: cmrdr
ports:
- "3306:3306"
version: '2'
services:
cameradar:
build: .
container_name: cameradar
volumes:
- "./cameradar_thumbnails:/tmp/thumbs"
- ".:/tmp/shared"
environment:
- CAMERAS_SUBNETWORKS=localhost
- CAMERAS_PORTS=554,8554
- CACHE_MANAGER=dumb
- MYSQL_ROOT_PASSWORD=root
cameradar-database:
container_name: cameradar-database
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=cmrdr
ports:
- "3306:3306"
volumes:
mysql_data:
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
ESC_SEQ="\x1b["
COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
# if command starts with an option, prepend /cameradar/bin/cameradar
if [ "${1:0:1}" = '-' ]; then
set -- /cameradar/bin/cameradar "$@"
fi
# skip setup if they want an option that stops cameradar
wantHelp=
for arg; do
case "$arg" in
-v|-h)
wantHelp=1
break
;;
esac
done
if [ "$CACHE_MANAGER" == "" ]; then
export CACHE_MANAGER="dumb"
fi
if [ "$CAMERAS_SUBNETWORKS" == "" ]; then
export CAMERAS_SUBNETWORKS="0.0.0.0"
fi
if [ "$CAMERAS_PORTS" == "" ]; then
export CAMERAS_PORTS="554,8554"
fi
envsubst < /cameradar/conf/cameradar.tmpl.conf.json > /cameradar/conf/cameradar.conf.json
if [ "$CACHE_MANAGER" == "mysql" ] && [ "$1" = '/cameradar/bin/cameradar' -a -z "$wantHelp" ]; then
echo -n "Waiting for cameradar-database to be ready..."
while ! mysqladmin ping -h "cameradar-database" -P3306 --silent; do
sleep 1; echo -n "."
done
echo -e $COL_GREEN"ok"$COL_RESET
echo "Cameradar init finished. Starting it."
fi
exec "$@"
-2
View File
@@ -1,2 +0,0 @@
CAMERAS_SUBNETWORKS=172.16.100.0/24,192.168.178.47
CAMERAS_PORTS=554,8554
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
ESC_SEQ="\x1b["
COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
COL_BLUE=$ESC_SEQ"34;01m"
COL_MAGENTA=$ESC_SEQ"35;01m"
COL_CYAN=$ESC_SEQ"36;01m"
# declare usefuls vars
CONF=/conf/cameradar.conf.json
# copy configuration
cp /tmp/conf/* /conf/
echo -n "replacing cameras subnetworks in configuration "
sed -i s#__CAMERAS_SUBNETWORKS__#$CAMERAS_SUBNETWORKS#g $CONF
echo -e $COL_GREEN"ok"$COL_RESET
echo -n "replacing cameras ports in configuration "
sed -i s#__PORTS_TO_CHECK__#$CAMERAS_PORTS#g $CONF
echo -e $COL_GREEN"ok"$COL_RESET
# Replace ext_cctv_mysql with the IP address of your DB or the name of its Docker
# container. The container has to be linked in docker-compose.yml for cameradar
# to be able to interact with it.
echo -n "replacing mysql host and port in configuration "
sed -i s#__MYSQL_ADDR__#mysql#g $CONF
# Reaplce 3306 with the port of your DB
sed -i s#__MYSQL_PORT__#3306#g $CONF
echo -e $COL_GREEN"ok"$COL_RESET
echo -n "waiting for mysql to be ready "
while ! mysqladmin ping -h"mysql" -P3306 --silent; do
sleep 1
done
echo -e $COL_GREEN"ok"$COL_RESET
/cameradar/bin/cameradar -l 1 -c /conf/cameradar.conf.json &
cameradar_pid=$!
trap 'kill -2 $cameradar_pid; wait $cameradar_pid; exit $?' SIGTERM SIGINT
wait $cameradar_pid
+9 -7
View File
@@ -2,14 +2,14 @@ FROM ubuntu:15.10
MAINTAINER brendan.leglaunec@etixgroup.com
ENV LD_LIBRARY_PATH="/cctv/libraries"
ENV LD_LIBRARY_PATH="/cameradar/libraries"
# install go
RUN apt-get update && apt-get install -y make git wget curl
RUN wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
# set variable env
ENV GOPATH=/go
ENV GOPATH=/cameradartest/go
ENV PATH=$PATH:/go/bin
ENV PATH=$PATH:/usr/local/go/bin
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
@@ -27,17 +27,19 @@ RUN apt-get update && apt-get install -y \
RUN apt-get install -y psmisc
ADD cctv_*_Debug_Linux.tar.gz /
RUN mv cctv_*_Debug_Linux cctv
ADD cameradar_*_Debug_Linux.tar.gz /
RUN mv cameradar_*_Debug_Linux cameradar
# create cameradaratest folder in go src path
RUN mkdir -p /go/src/cameradartest
ADD ./conf /conf
RUN mkdir -p /cameradartest/go/src/cameradartest
COPY src/*.go /cameradartest/go/src/cameradartest/
COPY ./conf /conf
ADD ./docker/run_cameradartest.sh /run.sh
# get go deps
RUN go get github.com/go-sql-driver/mysql
RUN mkdir /thumbnails
WORKDIR /go/src/cameradartest
WORKDIR /cameradartest/go/src/cameradartest
RUN go build -o cameradartest *.go
CMD ["/run.sh"]
+16 -7
View File
@@ -1,14 +1,23 @@
FROM ubuntu:16.04
MAINTAINER brendan.leglaunec@etixgroup.com
RUN useradd -m vlc; \
apt-get update; \
apt-get install -y vlc-nox
RUN sed -i s/geteuid/getppid/g /usr/bin/vlc
RUN apt-get update && apt-get install -y \
libgstrtspserver-1.0-dev \
libgstreamer1.0-dev \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
gstreamer1.0-tools \
libssl-dev mysql-client \
gstreamer1.0-plugins-good \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev
ADD ./docker/screen.png /vlc/screen.png
COPY ./docker/run_vlc.sh /start.sh
COPY ./etix_rtsp_server /etix_rtsp_server
COPY ./docker/run_ces.sh /start.sh
COPY ./camera_emulation_server /camera_emulation_server
EXPOSE 8554
RUN ./camera_emulation_server&
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
ESC_SEQ="\x1b["
COL_RESET=$ESC_SEQ"39;49;00m"
COL_RED=$ESC_SEQ"31;01m"
COL_GREEN=$ESC_SEQ"32;01m"
COL_YELLOW=$ESC_SEQ"33;01m"
echo -e $COL_YELLOW"Deleting old package ... "$COL_RESET
rm -f cameradar_*_${1:-"Release"}_Linux.tar.gz
echo -e $COL_GREEN"OK!"$COL_RESET
echo -e $COL_YELLOW"Creating package ... "$COL_RESET
cd ..
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
mkdir build
cd build
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
rm -f cameradar_*_${1:-"Release"}_Linux.tar.gz
cmake .. -DCMAKE_BUILD_TYPE=${1:-"Release"}
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
make package
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
cp cameradar_*_${1:-"Release"}_Linux.tar.gz ../test
cd ../test
ret=$?
if [ "$ret" -ne "0" ]; then
echo -e $COL_RED"KO!"$COL_RESET;
exit 1;
fi
echo -e $COL_GREEN"OK!"$COL_RESET
BIN
View File
Binary file not shown.
+10 -8
View File
@@ -1,14 +1,16 @@
{
"mysql_db" : {
"host" : "0.0.0.0",
"host" : "cameradar-database",
"port" : 3306,
"user": "root",
"password": "root",
"db_name": "cctv"
"db_name": "cmrdr"
},
"subnets" : "172.16.100.13 localhost",
"ports" : "554,8554", // if not specified, default will be 1-65535
"rtsp_url_file" : "conf/url.json",
"rtsp_ids_file" : "conf/ids.json",
"thumbnail_storage_path" : "/ce/que/tu/veux"
}
"subnets" : "localhost",
"ports" : "554,8554",
"rtsp_url_file" : "/conf/url.json",
"rtsp_ids_file" : "/conf/ids.json",
"thumbnail_storage_path" : "/tmp",
"cache_manager_path" : "/cameradar/cache_managers",
"cache_manager_name" : "dumb"
}
+13 -13
View File
@@ -2,19 +2,19 @@
"Output": "cameratest.log.xml",
"Cameradar" : {
"Path": "/home/ullaakut/Work/cctv_server2/cameradar/test/cameradar",
"Args": "-l 1 -c tmp_config",
"Ports": "554,5554,8554",
"IdsPath": "conf/ids.json",
"RoutesPath": "conf/url.json",
"ThumbPath": "/home/ullaakut/.cctv",
"dbHost": "0.0.0.0",
"dbPort": 3306,
"dbUser": "root",
"dbPassword": "root",
"dbName": "cctv",
"Console": false
},
"Path": "/cameradar/cameradar_standalone/cameradar",
"Args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
"Ports": "554,5554,8554",
"IdsPath": "/conf/ids.json",
"RoutesPath": "/conf/url.json",
"ThumbPath": "/tmp",
"dbHost": "cameradar-database",
"dbPort": 3306,
"dbUser": "root",
"dbPassword": "root",
"dbName": "cmrdr",
"Console": false
},
"Tests" : [
{
"address" : "127.0.0.1",
+17 -16
View File
@@ -1,18 +1,19 @@
{
"Output": "cameratest.log.xml",
"Cameradar" : {
"Path": "/cctv/bin/cameradar",
"Args": "-l 1 -c tmp_config",
"Ports": "554,5554,8554,5548",
"IdsPath": "/conf/ids.json",
"RoutesPath": "/conf/url.json",
"ThumbPath": "/thumbnails",
"dbHost": "mysql_cameradar",
"dbPort": 3306,
"dbUser": "root",
"dbPassword": "root",
"dbName": "cctv",
"Console": false
"output": "test-results.xml",
"cameradar" : {
"path": "/cameradar/bin/cameradar",
"args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
"ports": "554,5554,8554",
"ids_path": "conf/ids.json",
"routes_path": "conf/url.json",
"thumb_path": "/tmp",
"db_host": "cameradar-database",
"db_port": 3306,
"db_user": "root",
"db_password": "root",
"db_name": "cmrdr",
"console": false
},
"Tests" : __CAMERAS__
}
"tests" : __CAMERAS__
}
+10 -6
View File
@@ -3,7 +3,7 @@
ports=('8554' '8554' '8554' '8554' '8554' '8554')
users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '')
passwords=('admin' 'root' '12345' 'ubnt' 'password' '')
routes=('live.sdp' 'live.sdp' 'ch001.sdp' '' 'invalid' 'live_mpeg4.sdp')
routes=('cam0_0' 'live.sdp' 'ch001.sdp' 'cam' 'invalid' 'live_mpeg4.sdp')
cams_name_pattern="fake_camera_"
# json generation variable only
@@ -11,11 +11,16 @@ json="[\n"
first=true
# $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid
function make_json {
# Get all data about the container, this will return three lines
# One empty that we ignore
# the two other ones with the IP of our container
# We take the second one using sed and cut to get only the IPAddress
address="$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $CID)"
if [ "$first" = true ] ; then first=false
else json="$json,\n"; fi
json="$json{"
json="$json\"address\":\"$1\","
json="$json\"port\":\"$2\","
json="$json\"address\":\"$address\","
json="$json\"port\":$2,"
json="$json\"route\":\"$3\","
json="$json\"username\":\"$4\","
json="$json\"password\":\"$5\","
@@ -50,13 +55,12 @@ function start {
# if conf_idx = 4 -> invalid conf
if [ "$conf_idx" == "4" ] ; then is_valid=false; fi
docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route"
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid
CID=$(docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route");
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid $CID
done
# finalize json
json="$json]"
echo "$json"
}
function stop {
+14 -4
View File
@@ -1,15 +1,25 @@
#!/bin/bash
while ! mysqladmin ping -h"mysql_cameradar" -P3306 --silent; do
while ! mysqladmin ping -h"cameradar-database" -P3306 --silent; do
sleep 1
done
ls -alhR /conf
cat /etc/hosts
cat /tmp/tests/cameradartest.conf.json
# build
go build
cp /tmp/tests/*.xml ./
# run test
./cameradartest /tmp/tests/cameradartest.conf.json
cp cameratest.log.xml /tmp/tests/
ret=$?
echo "Tests exited with code ${ret}"
cat *.xml
cp *.xml /tmp/tests/
exit $ret
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
port=$1
user=$2
passw=$3
route=$4
url=""
# need first argument at least
if [ "$2" == "" ]; then
url="rtsp://:$port/$route"
else
url="rtsp://$user:$passw@:$port/$route"
fi
if [ "$2" == "" && "$3" == "" ]; then
./camera_emulation_server -r $4
else
./camera_emulation_server -u $2 -p $3 -r $4
fi
echo "Stream started on ${url}"
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
port=$1
user=$2
passw=$3
route=$4
url=""
# need first argument at least
if [ "$2" == "" ]; then
url="rtsp://:$port/$route"
else
url="rtsp://$user:$passw@:$port/$route"
fi
./etix_rtsp_server -u $s -p $3 -r $4
# cvlc /vlc/screen.png -I dummy --sout-keep --no-drop-late-frames --no-skip-frames --image-duration 9999 --sout="#transcode{vcodec=h264,fps=15,venc=x264{preset=ultrafast,tune=zerolatency,keyint=30,bframes=0,ref=1,level=30,profile=baseline,hrd=cbr,crf=20,ratetol=1.0,vbv-maxrate=1200,vbv-bufsize=1200,lookahead=0}}:rtp{sdp=$url}" --sout-all
Binary file not shown.
+21 -7
View File
@@ -1,3 +1,17 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
@@ -6,27 +20,27 @@ import (
"os"
)
func (m *manager) parseConfig() bool {
func (t *Tester) parseConfig() bool {
// Get config file path
confPath := "conf/cameratest.conf.json"
av := len(os.Args)
if av == 2 {
if av > 1 {
confPath = os.Args[1]
}
// Load config
fmt.Printf("Loading config file: %s ... ", confPath)
fmt.Printf("Loading Tester configuration file: %s ... ", confPath)
configFile, err := os.Open(confPath)
if err != nil {
fmt.Printf("\nCan't open config file: %s\n", err)
fmt.Printf("\nCan't open Tester configuration file: %s\n", err)
return false
}
dec := json.NewDecoder(configFile)
if err = dec.Decode(&m); err != nil {
fmt.Printf("\nUnable to deserialize config file: %s\n", err)
if err = dec.Decode(&t); err != nil {
fmt.Printf("\nUnable to deserialize Tester configuration file: %s\n", err)
return false
}
fmt.Println("Configuration file successfully loaded")
fmt.Println("Tester configuration file successfully loaded")
return true
}
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"database/sql"
"fmt"
"strconv"
_ "github.com/go-sql-driver/mysql"
)
// MysqlDB contains the MySQL configuration
type MysqlDB struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
DbName string `json:"db_name"`
}
func (t *Tester) dropDB() bool {
dsn := t.DB.User + ":" + t.DB.Password + "@" + "tcp(" + t.DB.Host + ":" + strconv.Itoa(t.DB.Port) + ")/" + t.DB.DbName + "?charset=utf8"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println(err)
}
defer db.Close()
q := "DROP DATABASE " + t.DB.DbName + ";"
_, err = db.Exec(q)
if err != nil {
fmt.Println(err)
}
fmt.Println("------ Dropped Cameradar Database -------")
return true
}
func (t *Tester) configureDatabase(DataBase *MysqlDB) bool {
var db MysqlDB
db.Host = t.Cameradar.DbHost
db.Port = t.Cameradar.DbPort
db.User = t.Cameradar.DbUser
db.Password = t.Cameradar.DbPassword
db.DbName = t.Cameradar.DbName
*DataBase = db
return true
}
-24
View File
@@ -1,24 +0,0 @@
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"fmt"
"strconv"
)
func (m *manager) dropDB() bool {
dsn := m.DB.User + ":" + m.DB.Password + "@" + "tcp(" + m.DB.Host + ":" + strconv.Itoa(m.DB.Port) + ")/" + m.DB.Db_name + "?charset=utf8"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println(err)
}
defer db.Close()
q := "DROP DATABASE cctv;"
_, err = db.Exec(q)
if err != nil {
fmt.Println(err)
}
fmt.Println("------ Dropped CCTV Database -------")
return true
}
-24
View File
@@ -1,24 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"os"
)
// Launch it via goroutine
// Start read log of service
func getResult(test *[]Result, resultPath string) bool {
// Load config
resultFile, err := os.Open(resultPath)
if err != nil {
fmt.Printf("\nCan't open result file: %s\n", err)
return false
}
dec := json.NewDecoder(resultFile)
if err = dec.Decode(&test); err != nil {
fmt.Printf("\nUnable to deserialize result file: %s\n", err)
return false
}
return true
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"bufio"
"fmt"
"io"
)
// Launch it via goroutine
// Start read log of service
func readLog(service *Service, reader io.ReadCloser) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
str := scanner.Text()
if service.Console {
fmt.Printf("[%s] %s\n", service.Path, str)
}
fmt.Printf("%s\n", str)
service.Mutex.Lock()
service.Logs = append(service.Logs, str)
service.Mutex.Unlock()
}
if err := scanner.Err(); err != nil {
fmt.Printf("[%s] Service failed: %s\n", service.Path, err)
}
fmt.Printf("Logger of service: [%s] stopped\n", service.Path)
service.Active = false
}
+22 -6
View File
@@ -1,30 +1,46 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"fmt"
"os"
)
func main() {
manager := new(manager)
defer manager.Stop()
Tester := new(Tester)
defer Tester.Stop()
// Parse conf (streams should already be launched by Jenkins)
fmt.Println("--- Initializing Cameradar Test Tool ... ---")
if !manager.Init() {
if !Tester.Init() {
fmt.Println("-> Cameradar Test Tool initialization FAILED")
return
}
// Run tests
if !manager.Run() {
if !Tester.Run() {
fmt.Println("-> Cameradar Test Tool FAILED")
}
// Write results
fmt.Println("--- Writing results... ---")
if !manager.WriteResults(*(manager.Result), manager.Config.Output) {
if !Tester.WriteResults(*(Tester.Result), Tester.Output) {
fmt.Println("-> Write results FAILED")
return
os.Exit(1)
}
fmt.Println("--- Writing results done ---")
os.Exit(0)
}
-55
View File
@@ -1,55 +0,0 @@
package main
import (
"fmt"
"sync"
)
type manager struct {
Config
Tests []Result
Result *TestCase
DB mysql_db
}
// Config needs refacto
type Config struct {
Cameradar Service `json:"Cameradar"`
Output string
}
func (m *manager) Init() bool {
fmt.Println("- Parsing")
if !m.parseConfig() {
return false
}
fmt.Println("- Cleaning content")
killService(&m.Config.Cameradar)
return true
}
func (m *manager) Run() bool {
var wg sync.WaitGroup
fmt.Println("\n- Launching all tests")
var newTest = new(TestCase)
newTest.expected = m.Tests
if m.generateConfig(m.Tests, &m.DB) {
m.dropDB()
wg.Add(1)
go m.invokeTestCase(newTest, &wg)
m.Result = newTest
}
wg.Wait()
fmt.Printf("All tests completed\n")
return true
}
func (m *manager) Stop() bool {
killService(&m.Config.Cameradar)
return true
}
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"encoding/json"
"errors"
"fmt"
"os"
)
// Result contains the data of a Cameradar result, plus an error field in order to add error messages to the JUnit report
type Result struct {
Address string `json:"address"`
IDsFound bool `json:"ids_found"`
PathFound bool `json:"path_found"`
Password string `json:"password"`
Port int `json:"port"`
Route string `json:"route"`
ServiceName string `json:"service_name"`
Protocol string `json:"protocol"`
State string `json:"state"`
Username string `json:"username"`
Valid bool `json:"valid"`
Thumb string `json:"thumbnail_path"`
err error // in case of a fail, add a message
}
// Launch it via goroutine
// Start read log of service
func getResult(test *[]Result, resultPath string) bool {
// Load config
resultFile, err := os.Open(resultPath)
if err != nil {
fmt.Printf("\nCan't open result file: %s\n", err)
return false
}
dec := json.NewDecoder(resultFile)
if err = dec.Decode(&test); err != nil {
fmt.Printf("\nUnable to deserialize result file: %s\n", err)
return false
}
return true
}
func isValid(e *Result, r Result) bool {
if e.Username != r.Username {
e.err = errors.New(e.Address + " had a different username than " + r.Username)
return false
}
if e.Password != r.Password {
e.err = errors.New(e.Address + " had a different password than " + r.Password)
return false
}
if e.Port != r.Port {
e.err = errors.New(e.Address + " had a different port than expected")
return false
}
if e.Valid != r.Valid {
e.err = errors.New(e.Address + " had a different validity than expected")
return false
}
return true
}
// Extend needs refacto
func Extend(slice []Result, element Result) []Result {
n := len(slice)
if n == cap(slice) {
// Slice is full; must grow.
// We double its size and add 1, so if the size is zero we still grow.
newSlice := make([]Result, len(slice), 2*len(slice)+1)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0 : n+1]
slice[n] = element
return slice
}
+28 -16
View File
@@ -1,3 +1,17 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
@@ -9,24 +23,23 @@ import (
// Service needs refacto
type Service struct {
Path string `json:"Path"`
Args string `json:"Args"`
Ports string `json:"Ports"`
IdsPath string `json:"IdsPath"`
RoutesPath string `json:"RoutesPath"`
DbHost string `json:"dbHost"`
DbPort int `json:"dbPort"`
DbUser string `json:"dbUser"`
DbPassword string `json:"dbPassword"`
DbName string `json:"dbName"`
ThumbPath string `json:"ThumbPath"`
Console bool `json:"Console"`
Path string `json:"path"`
Args string `json:"args"`
Ports string `json:"ports"`
IdsPath string `json:"ids_path"`
RoutesPath string `json:"routes_path"`
ThumbPath string `json:"thumb_path"`
DbHost string `json:"db_host"`
DbPort int `json:"db_port"`
DbUser string `json:"db_user"`
DbPassword string `json:"db_password"`
DbName string `json:"db_name"`
Console bool `json:"console"`
Logs []string
Active bool // Based on io.ReadCloser status
Mutex sync.Mutex
cmd *exec.Cmd // Go handler of the service
Mutex sync.Mutex
cmd *exec.Cmd // Go handler of the service
}
func startService(service *Service) bool {
@@ -77,7 +90,6 @@ func killService(service *Service) {
fmt.Println(err)
}
// Sending SIGABORT, more reliable for VLC
sigAbort := []string{service.Path, "-s", "SIGABRT"}
fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Path)
cmd = exec.Command("killall", sigAbort...)
+61 -140
View File
@@ -1,173 +1,94 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"sync"
"time"
)
// MysqlDB needs refacto
type MysqlDB struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
DbName string `json:"db_name"`
}
// CameradarConfig needs refacto
type CameradarConfig struct {
MysqlDB MysqlDB `json:"mysql_db"`
Subnets string `json:"subnets"`
Ports string `json:"ports"`
RtspURLFile string `json:"rtsp_url_file"`
RtspIdsFile string `json:"rtsp_ids_file"`
ThumbnailStoragePath string `json:"thumbnail_storage_path"`
}
// Result needs refacto
type Result struct {
Address string `json:"address"`
Password string `json:"password"`
Port string `json:"port"`
Route string `json:"route"`
Username string `json:"username"`
Valid bool `json:"valid,omitempty"`
Thumb string `json:"thumbnail_path,omitempty"`
}
// TestCase needs refacto
type TestCase struct {
// Test represents a test launched with Cameradar
type Test struct {
expected []Result
result []Result
time time.Duration
ok bool
}
func removeResult(expected []Result, index int) []Result {
if len(expected) > 1 {
return append(expected[:index], expected[index+1:]...)
}
return []Result{}
}
// Invoke the test
// Wrap results in a TestResult object
func (m *manager) invokeTestCase(testCase *TestCase, wg *sync.WaitGroup) {
func (t *Tester) invokeTestCase(testCase *Test, wg *sync.WaitGroup) {
startTime := time.Now()
if m.runTestCase(testCase) {
testCase.time = time.Since(startTime)
testCase.ok = true
fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds())
} else {
testCase.time = time.Since(startTime)
testCase.ok = false
fmt.Printf("Test failed in %.6fs\n", testCase.time.Seconds())
}
t.runTestCase(testCase)
testCase.time = time.Since(startTime)
fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds())
wg.Done()
}
func (m *manager) runTestCase(test *TestCase) bool {
fmt.Printf("Test triggered\n")
Cameradar := m.Config.Cameradar
startService(&Cameradar)
for Cameradar.Active {
time.Sleep(5 * time.Millisecond)
// Checks all valid results that are supposed to match
// Adds them to the valid results and leave the failed
// ones in the expected slice
//
// Then, if the result did not match the expected but it was supposed to fail
// Add it to the valid results and remove it from the expected slice
func (t *Tester) runTestCase(test *Test) {
startService(&t.Cameradar)
for t.Cameradar.Active {
time.Sleep(25 * time.Millisecond)
}
found := 0
toFind := len(test.expected)
var validResults []Result
if getResult(&test.result, "result.json") {
// Check all valid resutls that are supposed to match
// Add them to the valid results and leave the failed
// ones in the expected slice
if getResult(&test.result, "/tmp/shared/result.json") {
for _, r := range test.result {
index := 0
r.Valid = true
for _, e := range test.expected {
e.Thumb = r.Thumb
var err error
var addr []string
addr, err = net.LookupHost(e.Address)
e.Address = addr[0]
if e == r {
_, err = os.Stat(r.Thumb)
if err == nil {
fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.")
found++
validResults = Extend(validResults, r)
test.expected = append(test.expected[:index], test.expected[index+1:]...)
break
} else {
fmt.Println("The result of ", r.Address, " seemed valid, but the thumbnails could not be generated by Cameradar.")
}
for index, e := range test.expected {
if e.Address == r.Address && isValid(&e, r) {
// _, err := os.Stat(r.Thumb)
// if err == nil) {
fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.")
validResults = Extend(validResults, r)
test.expected = removeResult(test.expected, index)
break
// } else {
// e.err = error{"The result of " + e.Address + " seemed valid, but the thumbnails could not be generated by Cameradar : " + err.Error()}
// }
}
index++
}
}
index := 0
// If the result did not match the expected but it was supposed to fail
// Add it to the valid results and remove it from the expected slice
for _, e := range test.expected {
// This is in order to avoid checking the same values twice
copy := test.expected
for index, e := range copy {
if !e.Valid {
found++
fmt.Println("The result of", e.Address, "successfully failed.")
validResults = Extend(validResults, e)
test.expected = append(test.expected[:index], test.expected[index+1:]...)
break
test.expected = removeResult(test.expected, index)
} else {
e.err = errors.New("The camera with the address " + e.Address + " was not found by cameradar")
test.expected = removeResult(test.expected, index)
test.expected = Extend(test.expected, e)
fmt.Println("Should have been valid but was not found : ", e.Address)
}
index++
}
// If we found all the expected results, return true
if found == toFind {
return true
}
test.result = validResults
}
return false
}
func (m *manager) generateConfig(test []Result, DataBase *mysql_db) bool {
var config CameradarConfig
var db mysql_db
db.Host = m.Config.Cameradar.DbHost
db.Port = m.Config.Cameradar.DbPort
db.User = m.Config.Cameradar.DbUser
db.Password = m.Config.Cameradar.DbPassword
db.Db_name = m.Config.Cameradar.DbName
for _, t := range test {
if len(config.Subnets) > 0 {
config.Subnets += ","
}
config.Subnets += t.Address
}
config.Mysql_db = db
config.Ports = m.Config.Cameradar.Ports
config.Rtsp_url_file = m.Config.Cameradar.RoutesPath
config.Rtsp_ids_file = m.Config.Cameradar.IdsPath
config.Thumbnail_storage_path = m.Config.Cameradar.ThumbPath
b, _ := json.Marshal(config)
fmt.Println(string(b))
err := ioutil.WriteFile("tmp_config", b, 0644)
if err != nil {
fmt.Println(err)
return false
}
*DataBase = db
return true
}
// Extend needs refacto
func Extend(slice []Result, element Result) []Result {
n := len(slice)
if n == cap(slice) {
// Slice is full; must grow.
// We double its size and add 1, so if the size is zero we still grow.
newSlice := make([]Result, len(slice), 2*len(slice)+1)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0 : n+1]
slice[n] = element
return slice
}
+67
View File
@@ -0,0 +1,67 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"fmt"
"sync"
)
// Tester is the structure that will manage the whole testing
type Tester struct {
Cameradar Service `json:"cameradar"`
Output string
Tests []Result
Result *Test
DB MysqlDB
}
// Init gets the testing configuration and makes sure that no other Cameradar service is running at the moment
func (t *Tester) Init() bool {
fmt.Println("- Parsing")
if !t.parseConfig() {
return false
}
fmt.Println("- Cleaning content")
killService(&t.Cameradar)
return true
}
// Run launches the tests that have been set up by the init method
func (t *Tester) Run() bool {
var wg sync.WaitGroup
fmt.Println("\n- Launching all tests")
var newTest = new(Test)
newTest.expected = t.Tests
if t.configureDatabase(&t.DB) {
t.dropDB()
wg.Add(1)
go t.invokeTestCase(newTest, &wg)
t.Result = newTest
}
wg.Wait()
fmt.Println("All tests completed")
return true
}
// Stop kills the service launched by the tester
func (t *Tester) Stop() bool {
killService(&t.Cameradar)
return true
}
+83 -75
View File
@@ -1,11 +1,27 @@
// Copyright 2016 Etix Labs
//
// 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.
package main
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"time"
)
////////////////////////////////////////////////
@@ -13,21 +29,23 @@ import (
// JUnitTestSuites is a collection of JUnit test suites.
type JUnitTestSuites struct {
XMLName xml.Name `xml:"testsuites"`
Suites []JUnitTestSuite
XMLName xml.Name `xml:"testsuites"`
TestSuites []JUnitTestSuite `xml:"testsuite"`
}
// JUnitTestSuite is a single JUnit test suite which may contain many
// testcases.
type JUnitTestSuite struct {
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Time string `xml:"time,attr"`
TestCases []JUnitTestCase
XMLName xml.Name `xml:"testsuite"`
Tests int `xml:"tests,attr"`
Failures int `xml:"failures,attr"`
Time string `xml:"time,attr"`
TestCases []JUnitTestCase `xml:"testcase"`
}
// JUnitTestCase is a single test case with its result.
type JUnitTestCase struct {
XMLName xml.Name `xml:"testcase"`
Message string `xml:"message,attr"`
Time string `xml:"time,attr"`
Failure *JUnitFailure `xml:"failure,omitempty"`
@@ -35,27 +53,24 @@ type JUnitTestCase struct {
// JUnitFailure contains data related to a failed test.
type JUnitFailure struct {
Message string `xml:"message,attr"`
Type string `xml:"type,attr"`
Contents string `xml:",chardata"`
XMLName xml.Name `xml:"failure"`
Message string `xml:"message,attr"`
Type string `xml:"type,attr"`
Contents string `xml:",chardata"`
}
func (m *manager) WriteResults(result TestCase, output string) bool {
fmt.Printf("Displaying results...\n")
// Write Console report
m.writeConsoleReport(result)
// Write XML report
// Open xml
// WriteResults will output the results in the standard output as well as concatenate them in an XML JUnit report
func (t *Tester) WriteResults(result Test, output string) bool {
file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("Error opening XML: %s\n", err)
return false
}
defer file.Close()
err = m.writeJUnitReportXML(result, file, output)
err = t.writeJUnitReportXML(result, file, output)
if err != nil {
fmt.Printf("Error writing XML: %s\n", err)
fmt.Printf("The tests were unsuccessful: %s\n", err)
return false
}
fmt.Printf("-> JUnit XML report written: %s\n", output)
@@ -63,42 +78,65 @@ func (m *manager) WriteResults(result TestCase, output string) bool {
}
// Write tests results under JUnit format on w
func (m *manager) writeJUnitReportXML(result TestCase, r io.ReadWriter, output string) error {
func (t *Tester) writeJUnitReportXML(result Test, rw io.ReadWriter, output string) error {
suites := JUnitTestSuites{}
dec := xml.NewDecoder(r)
if err := dec.Decode(&suites); err != nil {
fmt.Printf("\nUnable to deserialize XML log file: %s\n", err)
buf, err := ioutil.ReadFile(output)
dec := xml.NewDecoder(bytes.NewBufferString(string(buf)))
err = dec.Decode(&suites)
if err != nil {
fmt.Printf("\nUnable to deserialize %s file: %s\n", output, err)
}
ts := JUnitTestSuite{
Tests: len(result.result) + len(result.expected),
Failures: 0,
Failures: len(result.expected),
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
TestCases: []JUnitTestCase{},
}
// Run throught all iterations
testCase := JUnitTestCase{
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
Failure: nil,
for _, r := range result.result {
testCase := JUnitTestCase{
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
Failure: nil,
}
testCase.Message = "The stream " + r.Address + " had the expected result"
ts.TestCases = append(ts.TestCases, testCase)
}
if len(result.result) > 0 {
testCase.Message = "These streams matched what we expected:"
for _, e := range result.expected {
testCase := JUnitTestCase{
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
Failure: nil,
}
if e.err != nil {
testCase.Failure = &JUnitFailure{
Message: e.err.Error(),
Type: "",
}
}
ts.TestCases = append(ts.TestCases, testCase)
}
for _, success := range result.result {
testCase.Message += " " + success.Address
}
if !result.ok {
testCase.Failure = &JUnitFailure{
Message: "These streams did not match what we expected:",
Type: "",
successCount := 0
failureCount := 0
for _, test := range ts.TestCases {
if test.Failure != nil {
failureCount++
} else {
successCount++
}
}
for _, fail := range result.expected {
ts.Failures++
testCase.Failure.Message += " " + fail.Address
fmt.Println("--- Test summary ---")
if successCount > 0 {
fmt.Printf("Results: %d/%d (%d%%)\n", successCount, successCount+failureCount, successCount*100/(successCount+failureCount))
fmt.Printf("Time: %.6fs\n", result.time.Seconds())
} else {
fmt.Printf("No test in success\n")
}
ts.TestCases = append(ts.TestCases, testCase)
suites.Suites = append(suites.Suites, ts)
suites.TestSuites = append(suites.TestSuites, ts)
// Fix indent
bytes, err := xml.MarshalIndent(suites, "", "\t")
if err != nil {
@@ -112,38 +150,8 @@ func (m *manager) writeJUnitReportXML(result TestCase, r io.ReadWriter, output s
}
writer := io.Writer(w)
writer.Write(bytes)
if failureCount > 0 {
return errors.New("Some cameras were not successfully accessed.")
}
return nil
}
func (m *manager) writeConsoleReport(result TestCase) bool {
min := 50 * time.Hour
max := 0 * time.Second
total := 0 * time.Second
successCount := 0
failureCount := 0
if result.ok {
successCount++
total += result.time
if result.time < min {
min = result.time
}
if result.time > max {
max = result.time
}
} else {
failureCount++
}
fmt.Println("--- Test summary ---")
if successCount > 0 {
fmt.Printf("Results: %d/%d (%d%%)\n", successCount, successCount+failureCount, successCount*100/(successCount+failureCount))
fmt.Printf("Total time: %.6fs\n", total.Seconds())
fmt.Printf("Average time: %.6fs\n", total.Seconds()/float64(successCount))
fmt.Printf("Min time: %.6fs\n", min.Seconds())
fmt.Printf("Max time: %.6fs\n", max.Seconds())
} else {
fmt.Printf("No test in success\n")
}
return true
}
+14 -10
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# check if a debug package exist in the current folder
if ! ls ./cctv_*_Debug_Linux.tar.gz 1> /dev/null 2>&1; then
if ! ls ./cameradar_*_Debug_Linux.tar.gz 1> /dev/null 2>&1; then
(echo "no debug package in the current folder"; exit 137)
exit 137
fi
@@ -19,40 +19,44 @@ function make_docker_command {
done
# add mysql libk
cmd="$cmd --link=\"mysql_cameradar\""
cmd="$cmd --link=\"cameradar-database\""
# add cameradar srcs
cmd="$cmd -v \"`pwd`/src:/go/src/cameradartest\""
# add cmaeradar conf
cmd="$cmd -v \"`pwd`/:/tmp/tests\""
# add container name
cmd="$cmd -v \"`pwd`/:/tmp/shared\""
# add container name
cmd="$cmd cameradartest"
}
function start_test {
make_docker_command $1
./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json
make_docker_command $1
eval $cmd
ret=$?
./docker/gen_cameras.sh stop
return $ret
}
# build images
echo "building docker images"
# building fake-camera container
docker build -f Dockerfile-camera -t fake-camera .
docker build --no-cache -f Dockerfile-camera -t fake-camera .
# building cameradartest image
docker build -t cameradartest .
docker build --no-cache -t cameradartest .
# getting mysql
echo "starting mysql"
docker pull mysql:5.7
docker run --name mysql_cameradar -e MYSQL_DATABASE=cctv -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
docker run --name cameradar-database -e MYSQL_DATABASE=cmrdr -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
start_test 1
start_test 5
# start_test 10
# start_test 20
ret=$?
echo "Tests returned ${ret}"
# stop mysql
echo "stopping mysql"
docker rm -f mysql_cameradar
docker rm -f cameradar-database
exit $ret