Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3fcc7a39c | |||
| fd88e761e2 | |||
| 1af533a1d3 | |||
| 3510b98797 | |||
| 1e25be7ca5 | |||
| 6e06346685 | |||
| 1fc21f0906 | |||
| 369728f6c3 | |||
| 6f09f99eb8 | |||
| cb7531f93f | |||
| 78c7e17816 | |||
| d98d78cd73 | |||
| e9ffb44b45 | |||
| 9f85415d89 | |||
| c0d890acad | |||
| 553524ae43 | |||
| f80af6bd58 | |||
| 6c127e4cbe | |||
| c16c8c0aaa | |||
| be74c3c814 | |||
| 2f93ddd7e5 | |||
| 108f869a43 | |||
| 6685f74a90 | |||
| 37176292d0 | |||
| a49b8ef481 | |||
| 8e26751247 |
@@ -41,3 +41,7 @@ build/
|
|||||||
deps/boost/
|
deps/boost/
|
||||||
deps/jsoncpp/
|
deps/jsoncpp/
|
||||||
mysql-connector/
|
mysql-connector/
|
||||||
|
|
||||||
|
# Test
|
||||||
|
test/cameradartest.conf.json
|
||||||
|
test/cameradar_*_Debug_Linux.tar.gz
|
||||||
+58
@@ -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
|
||||||
@@ -2,6 +2,75 @@
|
|||||||
|
|
||||||
This file lists all versions of the repository and precises all changes.
|
This file lists all versions of the repository and precises all changes.
|
||||||
|
|
||||||
|
## v1.1.4
|
||||||
|
|
||||||
|
#### Minor changes :
|
||||||
|
* Simplified use of Docker image
|
||||||
|
* Renamed MySQL table name to be more explicit
|
||||||
|
* Refactoring of the Golang functional tester done
|
||||||
|
* The output was made more human readable
|
||||||
|
* Added automatic code quality checks for pull requests
|
||||||
|
* Added contribution documentation
|
||||||
|
* Updated dictionaries to add user suggestions for Chinese cameras
|
||||||
|
* Enhanced `result.json` file's format
|
||||||
|
|
||||||
|
#### Bugfixes :
|
||||||
|
* Fixed a bug in the functional testing in which if the `result.json` file was not formatted correctly, the test failed but was still considered a success.
|
||||||
|
|
||||||
|
## 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
|
## v1.1.0
|
||||||
|
|
||||||
#### Major changes :
|
#### Major changes :
|
||||||
|
|||||||
+5
-8
@@ -19,11 +19,9 @@ set (PROJECT_NAME cameradar)
|
|||||||
|
|
||||||
project (${PROJECT_NAME})
|
project (${PROJECT_NAME})
|
||||||
|
|
||||||
message ("Here")
|
|
||||||
|
|
||||||
set (${PROJECT_NAME}_VERSION_MAJOR 1)
|
set (${PROJECT_NAME}_VERSION_MAJOR 1)
|
||||||
set (${PROJECT_NAME}_VERSION_MINOR 1)
|
set (${PROJECT_NAME}_VERSION_MINOR 1)
|
||||||
set (${PROJECT_NAME}_VERSION_PATCH 0)
|
set (${PROJECT_NAME}_VERSION_PATCH 4)
|
||||||
set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}")
|
set (${PROJECT_NAME}_VERSION "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}${${PROJECT_NAME}_SUFFIX}")
|
||||||
|
|
||||||
find_package(Git REQUIRED)
|
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
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") #enable error coloration on gcc
|
||||||
|
|
||||||
# release specific flags
|
# 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
|
#debug specific flags
|
||||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
|
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_BINARIES} "")
|
||||||
set (${CAMERADAR_LIBRARIES} "")
|
set (${CAMERADAR_LIBRARIES} "")
|
||||||
|
|
||||||
#build cache managers
|
# Build cache managers
|
||||||
add_subdirectory (deps)
|
add_subdirectory (deps)
|
||||||
message ("Debug")
|
|
||||||
add_subdirectory (cameradar_standalone)
|
add_subdirectory (cameradar_standalone)
|
||||||
add_subdirectory (cache_managers)
|
add_subdirectory (cache_managers)
|
||||||
|
|
||||||
@@ -115,11 +112,11 @@ install (FILES ${CAMERADAR_CACHE_MANAGERS} DESTINATION cache_managers)
|
|||||||
install (FILES ${CAMERADAR_LIBRARIES} DESTINATION libraries)
|
install (FILES ${CAMERADAR_LIBRARIES} DESTINATION libraries)
|
||||||
install (DIRECTORY ${CMAKE_SOURCE_DIR}/deps/licenses DESTINATION libraries)
|
install (DIRECTORY ${CMAKE_SOURCE_DIR}/deps/licenses DESTINATION libraries)
|
||||||
|
|
||||||
# cpack configuration
|
# CPack configuration
|
||||||
include (InstallRequiredSystemLibraries)
|
include (InstallRequiredSystemLibraries)
|
||||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar")
|
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "cameradar")
|
||||||
set (CPACK_PACKAGE_VENDOR "Etix Labs")
|
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_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_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||||
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# Cameradar Contribution
|
||||||
|
|
||||||
|
This file will give you guidelines on how to contribute if you want to, and will list known contributors to this repo.
|
||||||
|
|
||||||
|
If you're not into software development or not into C++, you can still help. Updating the dictionaries for example, would be a really cool contribution! Just make sure the ids and routes you add are **default constructor credentials** and not custom credentials.
|
||||||
|
|
||||||
|
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) !
|
||||||
|
|
||||||
|
## Version 2.0.0
|
||||||
|
|
||||||
|
- *Cameradar* will become the name of the library.
|
||||||
|
- *Cameraccess* will be the name of the binary that uses Cameradar to _hack_ the cameras.
|
||||||
|
|
||||||
|
This quite big refactoring comes from the fact that most users who want to access cameras either want to launch it with the basic cache manager, mostly using the docker image already provided in this repository, or will not use it because it does not integrate into their software solution without sharing their database with Cameradar, which would cause issues with database migrations for example.
|
||||||
|
|
||||||
|
Transforming it into a library will allow developers to use it directly in their own code exactly as they want, allowing for a greater flexibility. The Cameraccess binary will then provide a simple use example as well as maintaining the current simple way of using Cameradar for non-developers.
|
||||||
|
|
||||||
|
This is quite a huge task compared to the tiny changes I usually do on Cameradar, so it might take a long time.
|
||||||
|
|
||||||
|
If you want to contribute, note that the develop will stay in 1.x until the 2.0.0 is released. A new development branch will be created especially for the 2.0 version, called `2.0.0` from which all work on the 2.0.0 version will be done until the 2.0.0 version is ready to replace the 1.x on the master and develop branches. The rest of the workflow is exactly the same as for the rest of the repository.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### Branches & issues
|
||||||
|
|
||||||
|
When an issue is opened, a branch will be automatically created. If you want to work on this issue, this is the branch you **have** to work on and create your pull request from.
|
||||||
|
|
||||||
|
**Always make sure you're not working on the same issue as someone else, by asking on the issue to be assigned to it.**
|
||||||
|
|
||||||
|
### Commit names
|
||||||
|
|
||||||
|
The name of the commits should always be `v[next version] : [name of the fixed issue]` (ex: `v1.1.4 : Removed unnecessary null pointer checks`), and each PR should only contain one single commit.
|
||||||
|
|
||||||
|
When working on your local branch, you can do as many commits as you want, obviously. The most important is that you **squash** your commits before creating your pull request.
|
||||||
|
|
||||||
|
In case you're not familiar with squashing, here is a simple way to do it :
|
||||||
|
|
||||||
|
+ On your branch, when everything is clean and working, launch `git log` and count the number of commits your branch is ahead from compared to the `develop` branch.
|
||||||
|
+ Then launch `git rebase -i HEAD~X`, X being the number of commits you want to squash. For example if I had 12 commits on my branch, I will squash all of them by writing `git rebase -i HEAD~12`.
|
||||||
|
+ This will open a file letting you decide what to do with the commits. You want to keep the first `pick` and write `s` instead of the other ones, s meaning squash.
|
||||||
|
+ If there are conflicts, you will fix them step by step by following what git tells you, it's pretty straight-forward.
|
||||||
|
+ If there are no conflicts or if they are resolved, git will let you edit the commit names. Don't forget to comment the commit names of the commits you squashed by adding a `#` character in front of the commit message.
|
||||||
|
+ Now launch `git log`, you should see only one commit by the name you chose during the rebase.
|
||||||
|
|
||||||
|
### Pull Requests
|
||||||
|
|
||||||
|
When your pull request is created, GitHub will first check for conflicts, Codacy will check the shell and C++ code's quality and then Travis CI will try to build and launch functional tests of your versions of Cameradar.
|
||||||
|
|
||||||
|
If GitHub reports conflicts with the develop branch, you should resolve them by yourself using your git command-line interface. The easiest and cleanest way is to use `git rebase -i origin/develop` and follow git's instructions.
|
||||||
|
If Codacy reports new issues, they will be added in the comments of the PR to let you know what you should fix.
|
||||||
|
If Travis CI reports errors, you should be able to view the logs [by clicking here](https://travis-ci.org/EtixLabs/cameradar/builds) and you should fix it. No PR will be merged before all tests are passing correctly.
|
||||||
|
|
||||||
|
### Coding guidelines
|
||||||
|
|
||||||
|
This part will tell you about what are the general coding guidelines I want to keep on this project.
|
||||||
|
|
||||||
|
#### C++
|
||||||
|
|
||||||
|
+ All C++ code has to be formatted using `clang-format`
|
||||||
|
+ The namespaces should be respected and new files should implement the same namespace structure as the other files
|
||||||
|
+ Forward declarations should be used as much as possible
|
||||||
|
+ Use smart pointers instead of raw pointers as much as possible
|
||||||
|
+ Each constructor with only one parameter which is not a copy or a move constructor must be marked explicit
|
||||||
|
+ Use C++11 specifiers as much as possible *(override, noexcept)*
|
||||||
|
+ Variable and function names must always be in *snake_case*.
|
||||||
|
|
||||||
|
#### Golang
|
||||||
|
|
||||||
|
+ All Golang code has to be formated using `gofmt`
|
||||||
|
+ Make sure you follow the Golang [best practices](https://golang.org/doc/effective_go.html)
|
||||||
|
|
||||||
|
#### Shell scripting
|
||||||
|
|
||||||
|
+ Just make sure Codacy does not trigger warnings on your code. I probably suck more than you in shell anyway, who would I be to give you guidelines on it?
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
+ **Brendan Le Glaunec** - [@Ullaakut](https://github.com/Ullaakut) - brendan.leglaunec@etixgroup.com : *Original developer & Maintainer*
|
||||||
|
+ **Jeremy Letang** - [@jeremyletang](https://github.com/jeremyletang) - letang.jeremy@gmail.com : *Idea of the project & Mentorship*
|
||||||
@@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
## An RTSP surveillance camera access multitool
|
## An RTSP surveillance camera access multitool
|
||||||
|
|
||||||
[](#license)
|
[](#license)
|
||||||
[](https://github.com/EtixLabs/cameradar/releases/latest)
|
[](https://hub.docker.com/r/ullaakut/cameradar/)
|
||||||
|
[](https://travis-ci.org/EtixLabs/cameradar)
|
||||||
|
[](https://www.codacy.com/app/brendan-le-glaunec/cameradar?utm_source=github.com&utm_medium=referral&utm_content=EtixLabs/cameradar&utm_campaign=Badge_Grade)
|
||||||
|
[](https://github.com/EtixLabs/cameradar/releases/latest)
|
||||||
|
|
||||||
|
|
||||||
#### Cameradar allows you to:
|
#### Cameradar allows you to:
|
||||||
@@ -24,6 +27,7 @@ Of course, you can also call for individual tasks if you plug in a Database to C
|
|||||||
|
|
||||||
## Table of content
|
## Table of content
|
||||||
|
|
||||||
|
- [Docker Image](#docker-image)
|
||||||
- [Quick install](#quick-install)
|
- [Quick install](#quick-install)
|
||||||
- [Dependencies](#quick-install###dependencies)
|
- [Dependencies](#quick-install###dependencies)
|
||||||
- [Five steps guide](#quick-install###five-steps-guide)
|
- [Five steps guide](#quick-install###five-steps-guide)
|
||||||
@@ -37,11 +41,32 @@ Of course, you can also call for individual tasks if you plug in a Database to C
|
|||||||
- [Output](#output)
|
- [Output](#output)
|
||||||
- [Check camera access](#check-camera-access)
|
- [Check camera access](#check-camera-access)
|
||||||
- [Command line options](#command-line-options)
|
- [Command line options](#command-line-options)
|
||||||
- [Under the hood](#under-the-hood)
|
|
||||||
- [Contribution](#contribution)
|
|
||||||
- [Next improvements](#next-improvements)
|
- [Next improvements](#next-improvements)
|
||||||
|
- [Contribution](#contribution)
|
||||||
|
- [Frequently Asked Questions](#frequently-asked-questions)
|
||||||
- [License](#license)
|
- [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
|
## 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.**
|
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.**
|
||||||
@@ -53,13 +78,16 @@ The only dependencies are `docker`, `docker-tools`, `git` and `make`.
|
|||||||
### Five steps guide
|
### Five steps guide
|
||||||
|
|
||||||
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
||||||
2. Go into the Cameradar repository, then to the `deployment` directory
|
2. `cd cameradar/deployment`
|
||||||
3. Tweak the `conf/cameradar.conf.json` as you need (see [the onfiguration guide here](#configuration) for more information)
|
3. Tweak the `conf/cameradar.conf.json` as you need (see [the configuration guide here](#configuration) for more information)
|
||||||
4. Run `docker-compose build cameradar` to build the cameradar container
|
4. `docker-compose build ; docker-compose up`
|
||||||
5. Run `docker-compose up cameradar` to launch Cameradar
|
|
||||||
|
|
||||||
By default, the version of the package in the deployment should be the last stable release.
|
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.
|
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
|
## Manual installation
|
||||||
@@ -82,12 +110,13 @@ To install Cameradar you will need these packages
|
|||||||
The simplest way would be to follow these steps :
|
The simplest way would be to follow these steps :
|
||||||
|
|
||||||
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
||||||
2. `mkdir build`
|
2. `cd cameradar`
|
||||||
3. `cd build`
|
3. `mkdir build`
|
||||||
3. `cmake ..`
|
4. `cd build`
|
||||||
4. `make`
|
5. `cmake ..`
|
||||||
5. `cd cameradar_standalone`
|
6. `make`
|
||||||
6. `./cameradar -s the_subnet_you_want_to_scan`
|
7. `cd cameradar_standalone`
|
||||||
|
8. `./cameradar -s the_subnet_you_want_to_scan`
|
||||||
|
|
||||||
## Advanced Docker deployment
|
## Advanced Docker deployment
|
||||||
|
|
||||||
@@ -99,7 +128,7 @@ The only dependencies are `docker` and `docker-compose`.
|
|||||||
|
|
||||||
### Using the package generation script
|
### Using the package generation script
|
||||||
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
||||||
2. `cd deployment`
|
2. `cd cameradar/deployment`
|
||||||
3. `rm *.tar.gz`
|
3. `rm *.tar.gz`
|
||||||
4. `./build_last_package.sh`
|
4. `./build_last_package.sh`
|
||||||
5. `docker-compose build cameradar`
|
5. `docker-compose build cameradar`
|
||||||
@@ -108,13 +137,15 @@ The only dependencies are `docker` and `docker-compose`.
|
|||||||
### Deploy a custom version of Cameradar by hand
|
### Deploy a custom version of Cameradar by hand
|
||||||
|
|
||||||
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
1. `git clone https://github.com/EtixLabs/cameradar.git`
|
||||||
2. `cd build`
|
2. `cd cameradar`
|
||||||
3. `cmake .. -DCMAKE_BUILD_TYPE=Release`
|
3. `mkdir build`
|
||||||
4. `make package`
|
4. `cd build`
|
||||||
5. `cp cameradar_*_Release_Linux.tar.gz ../deployment`
|
5. `cmake .. -DCMAKE_BUILD_TYPE=Release`
|
||||||
6. `cd ../deployment`
|
6. `make package`
|
||||||
7. `docker-compose build cameradar`
|
7. `cp cameradar_*_Release_Linux.tar.gz ../deployment`
|
||||||
8. `docker-compose up cameradar`
|
8. `cd ../deployment`
|
||||||
|
9. `docker-compose build cameradar`
|
||||||
|
10. `docker-compose up cameradar`
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
@@ -130,11 +161,11 @@ Here is the basic content of the configuration file with simple placeholders :
|
|||||||
},
|
},
|
||||||
"subnets" : "SUBNET1,SUBNET2,SUBNET3,[...]",
|
"subnets" : "SUBNET1,SUBNET2,SUBNET3,[...]",
|
||||||
"ports" : "PORT1,PORT2,[...]",
|
"ports" : "PORT1,PORT2,[...]",
|
||||||
"rtsp_url_file" : "conf/url.json",
|
"rtsp_url_file" : "/path/to/url/dictionary",
|
||||||
"rtsp_ids_file" : "conf/ids.json",
|
"rtsp_ids_file" : "/path/to/url/dictionary",
|
||||||
"thumbnail_storage_path" : "/valid/path/to/a/storage/directory",
|
"thumbnail_storage_path" : "/valid/path/to/a/storage/directory",
|
||||||
"cache_manager_path" : "../cache_managers/dumb_cache_manager",
|
"cache_manager_path" : "/path/to/cache/manager",
|
||||||
"cache_manager_name" : "dumb"
|
"cache_manager_name" : "CACHE_MANAGER_NAME"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -190,11 +221,11 @@ For each camera, Cameradar will output these JSON objects :
|
|||||||
|
|
||||||
## Check camera access
|
## Check camera access
|
||||||
|
|
||||||
If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able to use the GUI to connect to the RTSP stream using this format : `username:password@address:port/route`
|
If you have [VLC Media Player](http://www.videolan.org/vlc/), you should be able to use the GUI to connect to the RTSP stream using this format : `rtsp://username:password@address:port/route`
|
||||||
|
|
||||||
With the above result, the RTSP URL would be `admin:123456@173.16.100.45:554/live.sdp`
|
With the above result, the RTSP URL would be `rtsp://admin:123456@173.16.100.45:554/live.sdp`
|
||||||
|
|
||||||
If you're still in your console however, you can go even faster by using **vlc in commmand-line** and just run `vlc username:password@address:port/route` with the camera's info instead of the placeholders.
|
If you're still in your console however, you can go even faster by using **vlc in commmand-line** and just run `vlc rtsp://username:password@address:port/route` with the camera's info instead of the placeholders.
|
||||||
|
|
||||||
## Command line options
|
## Command line options
|
||||||
|
|
||||||
@@ -222,44 +253,29 @@ 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
|
* 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
|
* **"-v"** : Display Cameradar's version
|
||||||
* **"-h"** : Display this help
|
* **"-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
|
|
||||||
|
|
||||||
Cameradar uses **nmap** to map all of the subnetworks you specified in the configuration file (_cameradar.conf.json_), then parses its result to get all of the open RTSP streams that were detected.
|
|
||||||
|
|
||||||
After that, it uses **cURL** to send requests to the cameras and to try routes and ids for each camera until it is accessed or until all of the most used routes/ids (that you can modify in _conf/ids.json_ and _conf/url.json_) were tried
|
|
||||||
|
|
||||||
Then, it uses **FFMPEG** to generate a lightweight thumbnail from the stream, which you could use to get a quick preview of the camera's view.
|
|
||||||
|
|
||||||
Finally, it tries to access the stream using a simple **Gstreamer pipeline** to check for the stream's encoding.
|
|
||||||
|
|
||||||
The output of Cameradar will be printed on the standard output and will also be accessible in the result.json file.
|
|
||||||
|
|
||||||
Cameradar uses **nmap** to map all of the subnetworks you specified in the configuration file (_cameradar.conf.json_), then parses its result to get all of the open RTSP streams that were detected.
|
|
||||||
|
|
||||||
After that, it uses **cURL** to send requests to the cameras and to try routes and ids for each camera until it is accessed or until all of the most used routes/ids (that you can modify in _conf/ids.json_ and _conf/url.json_) were tried
|
|
||||||
|
|
||||||
Then, it uses **FFMPEG** to generate a lightweight thumbnail from the stream, which you could use to get a quick preview of the camera's view.
|
|
||||||
|
|
||||||
Finally, it tries to access the stream using a simple **Gstreamer pipeline** to check for the stream's encoding.
|
|
||||||
|
|
||||||
The output of Cameradar will be printed on the standard output and will also be accessible in the result.json file.
|
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
Well there are many things we could code in order to add features to Cameradar. Adding other protocols than RTSP would be really cool, as well as making more generic cache managers. Improving Cameradar's performance or even the deployment could also be a great help!
|
See [the contribution document](/CONTRIBUTION.md) to get started.
|
||||||
|
|
||||||
If you're not into software development or not into C++, even updating the dictionaries would be a really cool contribution! Just make sure the ids and routes you add are **default constructor credentials** and not custom credentials.
|
## Frequently Asked Questions
|
||||||
|
|
||||||
If you have other cool ideas, feel free to share them with me at [brendan.leglaunec@etixgroup.com](mailto:brendan.leglaunec@etixgroup.com) !
|
> My camera's credentials are guessed by Cameradar but the RTSP URL is not!
|
||||||
|
|
||||||
## Next improvements
|
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.
|
||||||
- [x] Add a docker deployment to avoid the current deps hell
|
|
||||||
- [x] Development of a MySQL cache manager
|
> Cameradar does not detect any camera!
|
||||||
- [ ] Development of a JSON file cache manager
|
|
||||||
- [ ] Development of an XML file cache manager
|
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.
|
||||||
- [ ] Make a standalone docker image
|
|
||||||
- [ ] Push to DockerHub
|
> 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. Also, maybe your camera's credentials are not yet known, in which case if you find them it would be very nice to add them to the Cameradar dictionaries to help other people in the future.
|
||||||
|
|
||||||
|
> 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
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ db_connection::execute(const std::string& request) {
|
|||||||
return_value = { execute_result::no_row_updated, "No row updated" };
|
return_value = { execute_result::no_row_updated, "No row updated" };
|
||||||
}
|
}
|
||||||
} catch (sql::SQLException& e) { return_value = { execute_result::sql_error, e.what() }; }
|
} catch (sql::SQLException& e) { return_value = { execute_result::sql_error, e.what() }; }
|
||||||
if (stmt) { delete stmt; }
|
delete stmt;
|
||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
@@ -103,8 +103,7 @@ db_connection::query(const std::string& query) {
|
|||||||
} catch (sql::SQLException& e) {
|
} catch (sql::SQLException& e) {
|
||||||
return_value = { nullptr, execute_result::sql_error, e.what() };
|
return_value = { nullptr, execute_result::sql_error, e.what() };
|
||||||
}
|
}
|
||||||
|
delete stmt;
|
||||||
if (stmt) { delete stmt; }
|
|
||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace etix {
|
|||||||
namespace cameradar {
|
namespace cameradar {
|
||||||
|
|
||||||
const std::string mysql_cache_manager::create_table_query =
|
const std::string mysql_cache_manager::create_table_query =
|
||||||
"CREATE TABLE IF NOT EXISTS `results` ("
|
"CREATE TABLE IF NOT EXISTS `cameradar_results` ("
|
||||||
"`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, "
|
"`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, "
|
||||||
"`address` tinytext NOT NULL, "
|
"`address` tinytext NOT NULL, "
|
||||||
"`password` tinytext NOT NULL, "
|
"`password` tinytext NOT NULL, "
|
||||||
@@ -55,31 +55,31 @@ const std::string mysql_cache_manager::create_table_query =
|
|||||||
"PRIMARY KEY (`id`));";
|
"PRIMARY KEY (`id`));";
|
||||||
|
|
||||||
const std::string mysql_cache_manager::insert_with_id_query =
|
const std::string mysql_cache_manager::insert_with_id_query =
|
||||||
"INSERT INTO `%s`.`results`"
|
"INSERT INTO `%s`.`cameradar_results`"
|
||||||
" (`address`, `password`, `product`, `protocol`, `route`, `service_name`, `state`, "
|
" (`address`, `password`, `product`, `protocol`, `route`, `service_name`, `state`, "
|
||||||
"`thumbnail_path`, `username`, `port`, `ids_found`, `path_found`)"
|
"`thumbnail_path`, `username`, `port`, `ids_found`, `path_found`)"
|
||||||
" VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')";
|
" VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')";
|
||||||
|
|
||||||
const std::string mysql_cache_manager::update_result_query =
|
const std::string mysql_cache_manager::update_result_query =
|
||||||
"UPDATE `%s`.`results` SET"
|
"UPDATE `%s`.`cameradar_results` SET"
|
||||||
" `results`.`address` = '%s',"
|
" `cameradar_results`.`address` = '%s',"
|
||||||
" `results`.`password` = '%s',"
|
" `cameradar_results`.`password` = '%s',"
|
||||||
" `results`.`product` = '%s',"
|
" `cameradar_results`.`product` = '%s',"
|
||||||
" `results`.`protocol` = '%s',"
|
" `cameradar_results`.`protocol` = '%s',"
|
||||||
" `results`.`route` = '%s',"
|
" `cameradar_results`.`route` = '%s',"
|
||||||
" `results`.`service_name` = '%s',"
|
" `cameradar_results`.`service_name` = '%s',"
|
||||||
" `results`.`state` = '%s',"
|
" `cameradar_results`.`state` = '%s',"
|
||||||
" `results`.`thumbnail_path` = '%s',"
|
" `cameradar_results`.`thumbnail_path` = '%s',"
|
||||||
" `results`.`username` = '%s',"
|
" `cameradar_results`.`username` = '%s',"
|
||||||
" `results`.`port` = '%s',"
|
" `cameradar_results`.`port` = '%s',"
|
||||||
" `results`.`ids_found` = '%s',"
|
" `cameradar_results`.`ids_found` = '%s',"
|
||||||
" `results`.`path_found` = '%s'"
|
" `cameradar_results`.`path_found` = '%s'"
|
||||||
" WHERE `results`.`address` LIKE '%s'";
|
" WHERE `cameradar_results`.`address` LIKE '%s'";
|
||||||
|
|
||||||
const std::string mysql_cache_manager::exist_query =
|
const std::string mysql_cache_manager::exist_query =
|
||||||
"SELECT * FROM `%s`.`results` WHERE `results`.`address` = '%s'";
|
"SELECT * FROM `%s`.`cameradar_results` WHERE `cameradar_results`.`address` = '%s'";
|
||||||
|
|
||||||
const std::string mysql_cache_manager::get_results_query = "SELECT * FROM `%s`.`results`";
|
const std::string mysql_cache_manager::get_results_query = "SELECT * FROM `%s`.`cameradar_results`";
|
||||||
|
|
||||||
const std::string mysql_cache_manager::name = "mysql-cache-manager";
|
const std::string mysql_cache_manager::name = "mysql-cache-manager";
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
include (find_sources)
|
include (find_sources)
|
||||||
find_sources ("src" "include" "src/models" "src/repositories" "src/tasks")
|
find_sources ("src" "include" "src/tasks")
|
||||||
|
|
||||||
add_executable (cameradar ${SOURCES})
|
add_executable (cameradar ${SOURCES})
|
||||||
target_link_libraries (cameradar pthread jsoncpp dl curl ${GSTREAMER_LIBRARIES})
|
target_link_libraries (cameradar pthread jsoncpp dl curl ${GSTREAMER_LIBRARIES})
|
||||||
|
|||||||
@@ -1,25 +1,16 @@
|
|||||||
{
|
{
|
||||||
"mysql_db" : {
|
"mysql_db" : {
|
||||||
"host" : "0.0.0.0",
|
"host" : "cameradar-database",
|
||||||
"port" : 3306,
|
"port" : 3306,
|
||||||
"user": "root",
|
"user": "root",
|
||||||
"password": "root",
|
"password": "root",
|
||||||
"db_name": "cctv_dev"
|
"db_name": "cmrdr"
|
||||||
},
|
},
|
||||||
|
"subnets" : "localhost",
|
||||||
"subnets" : "172.16.100.11",
|
|
||||||
|
|
||||||
// If not specified, will scan all ports (1-65535)
|
|
||||||
"ports" : "554,8554",
|
"ports" : "554,8554",
|
||||||
"rtsp_url_file" : "conf/url.json",
|
"rtsp_url_file" : "/cameradar/conf/url.json",
|
||||||
"rtsp_ids_file" : "conf/ids.json",
|
"rtsp_ids_file" : "/cameradar/conf/ids.json",
|
||||||
|
"thumbnail_storage_path" : "/tmp/thumbs",
|
||||||
// You must give an accessible path to an already existing directory
|
"cache_manager_path" : "/cameradar/cache_managers",
|
||||||
"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",
|
|
||||||
"cache_manager_name" : "dumb"
|
"cache_manager_name" : "dumb"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@
|
|||||||
"/play1.sdp",
|
"/play1.sdp",
|
||||||
"/play2.sdp",
|
"/play2.sdp",
|
||||||
"/rtpvideo1.sdp",
|
"/rtpvideo1.sdp",
|
||||||
|
"/rtsp_live0",
|
||||||
|
"/rtsp_live1",
|
||||||
|
"/rtsp_live2",
|
||||||
"/rtsp_tunnel",
|
"/rtsp_tunnel",
|
||||||
"/rtsph264",
|
"/rtsph264",
|
||||||
"/stream1",
|
"/stream1",
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ namespace tool {
|
|||||||
namespace encode {
|
namespace encode {
|
||||||
|
|
||||||
std::string encode64(const std::string& str_to_encode);
|
std::string encode64(const std::string& str_to_encode);
|
||||||
std::string decode64(const std::string& str_to_decode);
|
|
||||||
|
|
||||||
std::string base64_encode(unsigned char const*, unsigned int len);
|
std::string base64_encode(unsigned char const*, unsigned int len);
|
||||||
std::string base64_decode(std::string const& s);
|
std::string base64_decode(std::string const& s);
|
||||||
|
|||||||
@@ -14,14 +14,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
namespace etix {
|
namespace etix {
|
||||||
|
|
||||||
namespace tool {
|
namespace tool {
|
||||||
|
|
||||||
|
enum class loglevel { DEBUG = 1, INFO = 2, WARN = 4, ERR = 5, CRITICAL = 6 };
|
||||||
|
|
||||||
inline std::string
|
inline std::string
|
||||||
format_output(const std::string& from, const std::string& message) {
|
format_output(const std::string& from, const std::string& message) {
|
||||||
auto ss = std::stringstream{};
|
auto ss = std::stringstream{};
|
||||||
@@ -32,8 +34,6 @@ format_output(const std::string& from, const std::string& message) {
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class loglevel { DEBUG = 1, INFO = 2, WARN = 4, ERR = 5, CRITICAL = 6 };
|
|
||||||
|
|
||||||
class logger {
|
class logger {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::shared_ptr<spdlog::logger> console;
|
std::shared_ptr<spdlog::logger> console;
|
||||||
@@ -64,11 +64,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
|
||||||
get_name() const {
|
|
||||||
return this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
info(const std::string& message) {
|
info(const std::string& message) {
|
||||||
etix::tool::logger::get_instance().console->info(message);
|
etix::tool::logger::get_instance().console->info(message);
|
||||||
@@ -84,11 +79,6 @@ public:
|
|||||||
etix::tool::logger::get_instance().console->error(message);
|
etix::tool::logger::get_instance().console->error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
crit(const std::string& message) {
|
|
||||||
etix::tool::logger::get_instance().console->critical(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
debug(const std::string& message) {
|
debug(const std::string& message) {
|
||||||
etix::tool::logger::get_instance().console->debug(message);
|
etix::tool::logger::get_instance().console->debug(message);
|
||||||
@@ -111,6 +101,3 @@ public:
|
|||||||
#define LOG_INFO_(message, from) \
|
#define LOG_INFO_(message, from) \
|
||||||
etix::tool::logger::get_instance().info(etix::tool::format_output( \
|
etix::tool::logger::get_instance().info(etix::tool::format_output( \
|
||||||
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
|
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
|
||||||
#define LOG_CRIT_(message, from) \
|
|
||||||
etix::tool::logger::get_instance().crit(etix::tool::format_output( \
|
|
||||||
std::string(from) + "::" + __FUNCTION__ + ":" + std::to_string(__LINE__), message))
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
namespace etix {
|
namespace etix {
|
||||||
namespace cameradar {
|
namespace cameradar {
|
||||||
|
|
||||||
|
static const std::string default_result_file_path = "/tmp/shared/result.json";
|
||||||
|
|
||||||
class print : public etix::cameradar::cameradar_task {
|
class print : public etix::cameradar::cameradar_task {
|
||||||
const configuration& conf;
|
const configuration& conf;
|
||||||
std::shared_ptr<cache_manager> cache;
|
std::shared_ptr<cache_manager> cache;
|
||||||
|
|||||||
@@ -13,10 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "cachemanager.h" // for cache_manager
|
#include "cachemanager.h" // for cache_manager
|
||||||
|
#include <algorithm> // for move
|
||||||
#include <dlfcn.h> // for dlerror, dlclose, dlopen, dlsym, etc
|
#include <dlfcn.h> // for dlerror, dlclose, dlopen, dlsym, etc
|
||||||
#include <logger.h> // for LOG_ERR_
|
#include <logger.h> // for LOG_ERR_
|
||||||
#include <stdbool.h> // for bool, false, true
|
#include <stdbool.h> // for bool, false, true
|
||||||
#include <algorithm> // for move
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
@@ -46,10 +46,7 @@ cache_manager::cache_manager(cache_manager&& old)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache_manager::~cache_manager() {
|
cache_manager::~cache_manager() {
|
||||||
if (this->ptr) {
|
delete this->ptr;
|
||||||
delete this->ptr;
|
|
||||||
this->ptr = nullptr;
|
|
||||||
}
|
|
||||||
if (this->handle) { dlclose(handle); }
|
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; }
|
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&
|
||||||
cache_manager_base::get_instance() {
|
cache_manager_base::get_instance() {
|
||||||
|
|||||||
@@ -92,12 +92,12 @@ bool
|
|||||||
configuration::load_url() {
|
configuration::load_url() {
|
||||||
std::string content;
|
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()) {
|
if (this->rtsp_url_file.size()) {
|
||||||
content = read_file(this->rtsp_url_file.c_str()).second;
|
content = read_file(this->rtsp_url_file.c_str()).second;
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN_(
|
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 "
|
"the default one "
|
||||||
"instead.",
|
"instead.",
|
||||||
"configuration");
|
"configuration");
|
||||||
|
|||||||
@@ -13,17 +13,21 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <describe.h>
|
#include <describe.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace etix {
|
namespace etix {
|
||||||
namespace cameradar {
|
namespace cameradar {
|
||||||
|
|
||||||
|
std::mutex m;
|
||||||
|
|
||||||
// Ugly workaround
|
// Ugly workaround
|
||||||
size_t
|
size_t
|
||||||
write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
|
write_data(void* buffer, size_t size, size_t nmemb, void* userp) {
|
||||||
// I'm sorry for this
|
// I'm sorry for this
|
||||||
// Forget you ever saw it
|
// Forget you ever saw it
|
||||||
(void)buffer;
|
|
||||||
(void)userp;
|
(void)userp;
|
||||||
|
if (not buffer || not size || not nmemb) return 0;
|
||||||
|
|
||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +41,9 @@ curl_describe(const std::string& path, bool logs) {
|
|||||||
struct curl_slist* custom_msg = NULL;
|
struct curl_slist* custom_msg = NULL;
|
||||||
char URL[256];
|
char URL[256];
|
||||||
long rc;
|
long rc;
|
||||||
|
m.lock();
|
||||||
|
curl_global_init(0);
|
||||||
|
m.unlock();
|
||||||
csession = curl_easy_init();
|
csession = curl_easy_init();
|
||||||
if (csession == NULL) return -1;
|
if (csession == NULL) return -1;
|
||||||
sprintf(URL, "%s", path.c_str());
|
sprintf(URL, "%s", path.c_str());
|
||||||
@@ -78,7 +85,11 @@ curl_describe(const std::string& path, bool logs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_cleanup(csession);
|
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) {
|
if (logs) {
|
||||||
// Some cameras return 400 instead of 401, don't know why.
|
// Some cameras return 400 instead of 401, don't know why.
|
||||||
// Some cameras timeout and then curl considers the status as 0
|
// Some cameras timeout and then curl considers the status as 0
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
namespace etix {
|
namespace etix {
|
||||||
namespace cameradar {
|
namespace cameradar {
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
// The main loop of the binary
|
// The main loop of the binary
|
||||||
void
|
void
|
||||||
@@ -49,7 +49,7 @@ dispatcher::run() {
|
|||||||
// Waiting for task to cleanup / force stop command
|
// Waiting for task to cleanup / force stop command
|
||||||
while ((signal_handler::instance().should_stop() not_eq stop_priority::force_stop) and
|
while ((signal_handler::instance().should_stop() not_eq stop_priority::force_stop) and
|
||||||
doing_stuff()) {
|
doing_stuff()) {
|
||||||
std::this_thread::sleep_for(30ms);
|
std::this_thread::sleep_for(30ms);
|
||||||
}
|
}
|
||||||
worker.join();
|
worker.join();
|
||||||
}
|
}
|
||||||
@@ -63,8 +63,13 @@ dispatcher::do_stuff() {
|
|||||||
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
|
queue.push_back(new etix::cameradar::parsing(cache, conf, nmap_output));
|
||||||
}
|
}
|
||||||
if (opts.second.exist("-b")) {
|
if (opts.second.exist("-b")) {
|
||||||
queue.push_back(new etix::cameradar::brutelogs(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::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")) {
|
if (opts.second.exist("-t")) {
|
||||||
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
|
queue.push_back(new etix::cameradar::thumbnail(cache, conf, nmap_output));
|
||||||
@@ -76,8 +81,13 @@ dispatcher::do_stuff() {
|
|||||||
!opts.second.exist("-g")) {
|
!opts.second.exist("-g")) {
|
||||||
queue.push_back(new etix::cameradar::mapping(cache, conf, nmap_output));
|
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::parsing(cache, conf, nmap_output));
|
||||||
queue.push_back(new etix::cameradar::brutelogs(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::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::thumbnail(cache, conf, nmap_output));
|
||||||
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
|
queue.push_back(new etix::cameradar::stream_check(cache, conf, nmap_output));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ encode64(const std::string& str_to_encode) {
|
|||||||
str_to_encode.length());
|
str_to_encode.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
|
||||||
decode64(const std::string& str_to_decode) {
|
|
||||||
return base64_decode(str_to_decode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const std::string base64_chars =
|
static const std::string base64_chars =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
"abcdefghijklmnopqrstuvwxyz"
|
||||||
@@ -47,7 +42,6 @@ std::string
|
|||||||
base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
|
||||||
unsigned char char_array_3[3];
|
unsigned char char_array_3[3];
|
||||||
unsigned char char_array_4[4];
|
unsigned char char_array_4[4];
|
||||||
|
|
||||||
@@ -64,8 +58,9 @@ base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
if (i) {
|
if (i) {
|
||||||
for (j = i; j < 3; j++) char_array_3[j] = '\0';
|
for (int j = i; j < 3; j++) char_array_3[j] = '\0';
|
||||||
|
|
||||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||||
@@ -79,46 +74,6 @@ base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* from external source */
|
|
||||||
std::string
|
|
||||||
base64_decode(std::string const& encoded_string) {
|
|
||||||
int in_len = encoded_string.size();
|
|
||||||
int i = 0;
|
|
||||||
int j = 0;
|
|
||||||
int in_ = 0;
|
|
||||||
unsigned char char_array_4[4], char_array_3[3];
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
|
||||||
char_array_4[i++] = encoded_string[in_];
|
|
||||||
in_++;
|
|
||||||
if (i == 4) {
|
|
||||||
for (i = 0; i < 4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]);
|
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
for (i = 0; (i < 3); i++) ret += char_array_3[i];
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i) {
|
|
||||||
for (j = i; j < 4; j++) char_array_4[j] = 0;
|
|
||||||
|
|
||||||
for (j = 0; j < 4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]);
|
|
||||||
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "fs.h"
|
|
||||||
|
|
||||||
#include <vector> // for std::vector
|
|
||||||
#include <sstream> // for std::stringstream
|
|
||||||
#include <pwd.h> // for getpwuid, passwd
|
|
||||||
#include <stddef.h> // for size_t
|
|
||||||
#include <sys/stat.h> // for stat, mkdir, S_ISDIR
|
|
||||||
#include <unistd.h> // for getuid
|
|
||||||
#include <fstream> // for std::ifstream
|
|
||||||
|
|
||||||
namespace etix {
|
|
||||||
|
|
||||||
namespace tool {
|
|
||||||
|
|
||||||
std::vector<std::string>
|
|
||||||
split(const std::string& s, char delim) {
|
|
||||||
std::vector<std::string> elems;
|
|
||||||
std::stringstream ss(s);
|
|
||||||
|
|
||||||
std::string item;
|
|
||||||
while (std::getline(ss, item, delim)) elems.push_back(item);
|
|
||||||
|
|
||||||
return elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace fs {
|
|
||||||
|
|
||||||
fs_error
|
|
||||||
is_folder(const std::string& folder) {
|
|
||||||
struct stat sb;
|
|
||||||
|
|
||||||
if (stat(folder.c_str(), &sb) == 0) {
|
|
||||||
if (S_ISDIR(sb.st_mode))
|
|
||||||
return fs_error::is_dir;
|
|
||||||
else
|
|
||||||
return fs_error::is_not_dir;
|
|
||||||
}
|
|
||||||
return fs_error::dont_exist;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
get_or_create_folder(const std::string& folder) {
|
|
||||||
bool status = false;
|
|
||||||
|
|
||||||
switch (is_folder(folder)) {
|
|
||||||
case fs_error::is_dir: status = true; break;
|
|
||||||
case fs_error::is_not_dir: status = false; break;
|
|
||||||
case fs_error::dont_exist: status = create_recursive_folder(folder); break;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
create_folder(const std::string& folder) {
|
|
||||||
if (mkdir(folder.c_str(), 0755) == 0) { return true; }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
create_recursive_folder(const std::string& folder) {
|
|
||||||
auto path_elems = split(folder, '/');
|
|
||||||
std::string current_path = folder[0] == '/' ? "/" : "";
|
|
||||||
|
|
||||||
for (const auto& elem : path_elems) {
|
|
||||||
current_path += elem;
|
|
||||||
|
|
||||||
if (is_folder(current_path) == fs_error::dont_exist) create_folder(current_path);
|
|
||||||
|
|
||||||
current_path += '/';
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
get_file_folder(std::string full_file_path) {
|
|
||||||
// remove ending slash
|
|
||||||
if (full_file_path.back() == '/') full_file_path.pop_back();
|
|
||||||
|
|
||||||
size_t last_slash_position = full_file_path.find_last_of('/');
|
|
||||||
|
|
||||||
// it there is no slash, there is no folder to return
|
|
||||||
if (last_slash_position == std::string::npos) return "";
|
|
||||||
|
|
||||||
return std::string(full_file_path, 0, last_slash_position);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
home(void) {
|
|
||||||
struct passwd* passwdEnt = getpwuid(getuid());
|
|
||||||
return { passwdEnt->pw_dir };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
copy(const std::string& src, const std::string& dst) {
|
|
||||||
std::ifstream src_stream(src, std::ios::binary);
|
|
||||||
std::ofstream dst_stream(dst, std::ios::binary);
|
|
||||||
|
|
||||||
if (not src_stream.is_open()) return false;
|
|
||||||
|
|
||||||
dst_stream << src_stream.rdbuf();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // fs
|
|
||||||
|
|
||||||
} // tool
|
|
||||||
|
|
||||||
} // etix
|
|
||||||
@@ -14,12 +14,9 @@
|
|||||||
|
|
||||||
#include "version.h" // versionning
|
#include "version.h" // versionning
|
||||||
#include <dispatcher.h> // program loop
|
#include <dispatcher.h> // program loop
|
||||||
#include <fs.h> // fs::home
|
|
||||||
#include <iostream> // iostream
|
#include <iostream> // iostream
|
||||||
#include <opt_parse.h> // parsing opt
|
#include <opt_parse.h> // parsing opt
|
||||||
|
|
||||||
namespace cmrdr = etix::cameradar;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
print_version() {
|
print_version() {
|
||||||
std::cout << "Cameradar version " << CAMERADAR_VERSION << std::endl;
|
std::cout << "Cameradar version " << CAMERADAR_VERSION << std::endl;
|
||||||
@@ -41,6 +38,12 @@ parse_cmdline(int argc, char* argv[]) {
|
|||||||
opt_parse.optional("-g", "Check if the stream can be opened with GStreamer", false);
|
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("-v", "Display Cameradar's version", false);
|
||||||
opt_parse.optional("-h", "Display this help", 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();
|
opt_parse.execute();
|
||||||
|
|
||||||
if (opt_parse.exist("-h")) {
|
if (opt_parse.exist("-h")) {
|
||||||
@@ -82,14 +85,13 @@ check_storage_path(const std::string& thumbnail_storage_path) {
|
|||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char* argv[]) {
|
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);
|
auto args = parse_cmdline(argc, argv);
|
||||||
if (not args.first) return EXIT_FAILURE;
|
if (not args.first) return EXIT_FAILURE;
|
||||||
|
|
||||||
print_version();
|
print_version();
|
||||||
|
|
||||||
if (not args.second.exist("-l")) {
|
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");
|
LOG_INFO_("No log level set, using log level 1", "main");
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@@ -103,7 +105,7 @@ main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to load the configuration
|
// Try to load the configuration
|
||||||
auto conf = cmrdr::load(args);
|
auto conf = etix::cameradar::load(args);
|
||||||
if (not conf.first) { return EXIT_FAILURE; }
|
if (not conf.first) { return EXIT_FAILURE; }
|
||||||
|
|
||||||
LOG_INFO_("Configuration successfully loaded", "main");
|
LOG_INFO_("Configuration successfully loaded", "main");
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include <rtsp_path.h>
|
|
||||||
#include <logger.h>
|
#include <logger.h>
|
||||||
|
#include <rtsp_path.h>
|
||||||
|
|
||||||
namespace etix {
|
namespace etix {
|
||||||
|
|
||||||
@@ -21,10 +21,17 @@ namespace cameradar {
|
|||||||
|
|
||||||
const std::string
|
const std::string
|
||||||
make_path(const stream_model& model) {
|
make_path(const stream_model& model) {
|
||||||
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
|
if (model.password != "" || model.username != "") {
|
||||||
model.address + ":" + std::to_string(model.port) + model.route);
|
std::string ret(model.service_name + "://" + model.username + ":" + model.password + "@" +
|
||||||
LOG_DEBUG_(ret, "debug");
|
model.address + ":" + std::to_string(model.port) + model.route);
|
||||||
return ret;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ brutelogs::test_ids(const etix::cameradar::stream_model& stream,
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
std::string path = stream.service_name + "://";
|
std::string path = stream.service_name + "://";
|
||||||
if (username != "" || password != "") { path += username + ":" + password + "@"; }
|
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");
|
LOG_INFO_("Testing ids : " + path, "brutelogs");
|
||||||
try {
|
try {
|
||||||
if (curl_describe(path, true)) {
|
if (curl_describe(path, true)) {
|
||||||
|
|||||||
@@ -70,10 +70,7 @@ brutepath::bruteforce_camera(const stream_model& stream) const {
|
|||||||
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
if (signal_handler::instance().should_stop() != etix::cameradar::stop_priority::running)
|
||||||
break;
|
break;
|
||||||
if ((*cache)->has_changed(stream)) return true;
|
if ((*cache)->has_changed(stream)) return true;
|
||||||
if (test_path(stream, route)) {
|
if (test_path(stream, route)) return true;
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ parsing::parse_camera(TiXmlElement* xml_host, std::vector<stream_model>& data) c
|
|||||||
stream.service_name = "closed";
|
stream.service_name = "closed";
|
||||||
stream.product = "closed";
|
stream.product = "closed";
|
||||||
}
|
}
|
||||||
data.push_back(stream);
|
if (!stream.state.compare("open")) data.push_back(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,22 +23,23 @@ bool
|
|||||||
print::run() const {
|
print::run() const {
|
||||||
std::vector<stream_model> results = (*cache)->get_valid_streams();
|
std::vector<stream_model> results = (*cache)->get_valid_streams();
|
||||||
std::ofstream file;
|
std::ofstream file;
|
||||||
bool first = true;
|
|
||||||
file.open("result.json");
|
|
||||||
file << "[\n";
|
|
||||||
for (const auto& stream : results) {
|
|
||||||
LOG_INFO_("Found a valid RTSP Stream and generated a thumbnail at : " +
|
|
||||||
stream.thumbnail_path,
|
|
||||||
"print");
|
|
||||||
|
|
||||||
if (first)
|
file.open(default_result_file_path);
|
||||||
first = false;
|
if (file.fail()) {
|
||||||
else
|
LOG_ERR_("Result file could not be opened : " + default_result_file_path, "print");
|
||||||
file << ",";
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "[\n";
|
||||||
|
unsigned int i = 0;
|
||||||
|
for (const auto& stream : results) {
|
||||||
file << deserialize(stream).toStyledString();
|
file << deserialize(stream).toStyledString();
|
||||||
|
|
||||||
|
if (++i < results.size()) file << ",";
|
||||||
|
|
||||||
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(), "print");
|
LOG_INFO_("Generated JSON Result : " + deserialize(stream).toStyledString(), "print");
|
||||||
}
|
}
|
||||||
file << "\n]";
|
file << "]";
|
||||||
file.close();
|
file.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,21 +22,19 @@ namespace cameradar {
|
|||||||
// In order to check for the stream validity
|
// In order to check for the stream validity
|
||||||
bool
|
bool
|
||||||
stream_check::run() const {
|
stream_check::run() const {
|
||||||
GstElement* pipeline;
|
|
||||||
GstElement* elem;
|
GstElement* elem;
|
||||||
|
|
||||||
gst_init(nullptr, nullptr);
|
gst_init(nullptr, nullptr);
|
||||||
|
|
||||||
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
std::vector<stream_model> streams = (*cache)->get_valid_streams();
|
||||||
|
|
||||||
if (not streams.size()) {
|
if (not streams.size()) {
|
||||||
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
|
LOG_WARN_("There were no valid streams to check. Cameradar will stop.", "stream_check");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& stream : streams) {
|
for (const auto& stream : streams) {
|
||||||
GError* error = NULL;
|
GError* error = NULL;
|
||||||
|
|
||||||
pipeline =
|
GstElement* pipeline =
|
||||||
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
|
gst_parse_launch("rtspsrc name=source ! rtph264depay ! h264parse ! fakesink", &error);
|
||||||
|
|
||||||
std::string location = "rtsp://";
|
std::string location = "rtsp://";
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ thumbnail::generate_thumbnail(const stream_model& stream) const {
|
|||||||
return false;
|
return false;
|
||||||
std::string ffmpeg_cmd =
|
std::string ffmpeg_cmd =
|
||||||
"mkdir -p %s ; "
|
"mkdir -p %s ; "
|
||||||
|
"timeout 20 "
|
||||||
"ffmpeg "
|
"ffmpeg "
|
||||||
"-rtsp_transport tcp "
|
"-rtsp_transport tcp "
|
||||||
"-y "
|
"-y "
|
||||||
|
|||||||
+1
-2
@@ -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 "${SOURCE_DIR}/src/lib_json")
|
||||||
set (JSONCPP_LIBRARY_DIR ${JSONCPP_LIBRARY_DIR} PARENT_SCOPE)
|
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})
|
list (APPEND CAMERADAR_INSTALL_DEPENDENCIES ${JSONCPP_INSTALL_DEPENDENCIES})
|
||||||
|
|
||||||
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
|
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
# Copyright (C) 2015 Etix Labs - All Rights Reserved.
|
## Copyright 2016 Etix Labs
|
||||||
# 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
|
## Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# Dissemination of this information or reproduction of this material is strictly forbidden unless
|
## you may not use this file except in compliance with the License.
|
||||||
# prior written permission is obtained from Etix Labs.
|
## 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
|
# MySQL Connector dependency
|
||||||
message(STATUS "Configuring deps.mysqlconnector")
|
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)
|
set (MYSQL_CONNECTOR_LIBRARY_DIR ${MYSQL_CONNECTOR_LIBRARY_DIR} PARENT_SCOPE)
|
||||||
|
|
||||||
# list all the hiredis libraries
|
# list all the mysql libraries
|
||||||
file(GLOB MYSQL_CONNECTOR_INSTALL_DEPENDENCIES "${MYSQL_CONNECTOR_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlcppconn*${CMAKE_SHARED_LIBRARY_SUFFIX}*")
|
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})
|
list (APPEND CAMERADAR_INSTALL_DEPENDENCIES ${MYSQL_CONNECTOR_INSTALL_DEPENDENCIES})
|
||||||
|
|
||||||
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
|
set(CAMERADAR_INSTALL_DEPENDENCIES ${CAMERADAR_INSTALL_DEPENDENCIES} PARENT_SCOPE)
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ RUN apt-get update && apt-get install -y \
|
|||||||
ADD cameradar_*_Release_Linux.tar.gz /
|
ADD cameradar_*_Release_Linux.tar.gz /
|
||||||
RUN mv cameradar_*_Release_Linux cameradar
|
RUN mv cameradar_*_Release_Linux cameradar
|
||||||
|
|
||||||
RUN mkdir /conf
|
COPY conf /cameradar/conf
|
||||||
ADD run.sh /run.sh
|
|
||||||
|
|
||||||
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"]
|
||||||
|
|||||||
@@ -5,17 +5,51 @@ COL_RESET=$ESC_SEQ"39;49;00m"
|
|||||||
COL_RED=$ESC_SEQ"31;01m"
|
COL_RED=$ESC_SEQ"31;01m"
|
||||||
COL_GREEN=$ESC_SEQ"32;01m"
|
COL_GREEN=$ESC_SEQ"32;01m"
|
||||||
COL_YELLOW=$ESC_SEQ"33;01m"
|
COL_YELLOW=$ESC_SEQ"33;01m"
|
||||||
COL_BLUE=$ESC_SEQ"34;01m"
|
|
||||||
COL_MAGENTA=$ESC_SEQ"35;01m"
|
echo -e $COL_YELLOW"Deleting old package ... "$COL_RESET
|
||||||
COL_CYAN=$ESC_SEQ"36;01m"
|
rm -f cameradar_*_${1:-"Release"}_Linux.tar.gz
|
||||||
|
echo -e $COL_GREEN"OK!"$COL_RESET
|
||||||
|
|
||||||
echo -e $COL_YELLOW"Creating package ... "$COL_RESET
|
echo -e $COL_YELLOW"Creating package ... "$COL_RESET
|
||||||
{ cd ..
|
|
||||||
mkdir build
|
cd ..
|
||||||
cd build
|
ret=$?
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
if [ "$ret" -ne "0" ]; then
|
||||||
make package
|
echo -e $COL_RED"KO!"$COL_RESET;
|
||||||
cp cameradar_*_Release_Linux.tar.gz ../deployment
|
exit 1;
|
||||||
cd ../deployment
|
fi
|
||||||
} &> /dev/null
|
|
||||||
|
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
|
echo -e $COL_GREEN"OK!"$COL_RESET
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
"/camera.stm",
|
"/camera.stm",
|
||||||
"/ch0",
|
"/ch0",
|
||||||
"/ch001.sdp",
|
"/ch001.sdp",
|
||||||
|
"/ch01.264",
|
||||||
"/ch0_unicast_firststream",
|
"/ch0_unicast_firststream",
|
||||||
"/ch0_unicast_secondstream",
|
"/ch0_unicast_secondstream",
|
||||||
"/channel1",
|
"/channel1",
|
||||||
@@ -61,6 +62,9 @@
|
|||||||
"/play1.sdp",
|
"/play1.sdp",
|
||||||
"/play2.sdp",
|
"/play2.sdp",
|
||||||
"/rtpvideo1.sdp",
|
"/rtpvideo1.sdp",
|
||||||
|
"/rtsp_live0",
|
||||||
|
"/rtsp_live1",
|
||||||
|
"/rtsp_live2",
|
||||||
"/rtsp_tunnel",
|
"/rtsp_tunnel",
|
||||||
"/rtsph264",
|
"/rtsph264",
|
||||||
"/stream1",
|
"/stream1",
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
cameradar:
|
version: '2'
|
||||||
build: .
|
|
||||||
dockerfile: Dockerfile
|
services:
|
||||||
env_file: env_file
|
cameradar:
|
||||||
volumes:
|
build: .
|
||||||
- "./conf:/tmp/conf:ro"
|
container_name: cameradar
|
||||||
- "./cameradar_thumbnails:/tmp/cameradar_thumbnails"
|
volumes:
|
||||||
links:
|
- "./cameradar_thumbnails:/tmp/thumbs"
|
||||||
- mysql
|
- ".:/tmp/shared"
|
||||||
mysql:
|
environment:
|
||||||
image: mysql:5.7
|
- CAMERAS_SUBNETWORKS=localhost
|
||||||
environment:
|
- CAMERAS_PORTS=554,8554
|
||||||
MYSQL_ROOT_PASSWORD: root
|
- CACHE_MANAGER=dumb
|
||||||
MYSQL_DATABASE: cmrdr
|
- MYSQL_ROOT_PASSWORD=root
|
||||||
ports:
|
cameradar-database:
|
||||||
- "3306:3306"
|
container_name: cameradar-database
|
||||||
|
image: mysql:5.7
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=root
|
||||||
|
- MYSQL_DATABASE=cmrdr
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
|||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ESC_SEQ="\x1b["
|
||||||
|
COL_RESET=$ESC_SEQ"39;49;00m"
|
||||||
|
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 "$@"
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
CAMERAS_SUBNETWORKS=172.16.100.0/24,192.168.178.47
|
|
||||||
CAMERAS_PORTS=554,8554
|
|
||||||
@@ -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
|
|
||||||
+11
-12
@@ -2,19 +2,18 @@ FROM ubuntu:15.10
|
|||||||
|
|
||||||
MAINTAINER brendan.leglaunec@etixgroup.com
|
MAINTAINER brendan.leglaunec@etixgroup.com
|
||||||
|
|
||||||
ENV LD_LIBRARY_PATH="/cctv/libraries"
|
ENV LD_LIBRARY_PATH="/cameradar/libraries"
|
||||||
|
|
||||||
# install go
|
# Manually install go
|
||||||
RUN apt-get update && apt-get install -y make git wget curl
|
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 wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||||
RUN tar -C /usr/local -xzf 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:/go/bin
|
||||||
ENV PATH=$PATH:/usr/local/go/bin
|
ENV PATH=$PATH:/usr/local/go/bin
|
||||||
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||||
|
|
||||||
# needed for cameradar
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
nmap \
|
nmap \
|
||||||
libmysqlclient18 \
|
libmysqlclient18 \
|
||||||
@@ -27,17 +26,17 @@ RUN apt-get update && apt-get install -y \
|
|||||||
|
|
||||||
RUN apt-get install -y psmisc
|
RUN apt-get install -y psmisc
|
||||||
|
|
||||||
ADD cctv_*_Debug_Linux.tar.gz /
|
ADD cameradar_*_Debug_Linux.tar.gz /
|
||||||
RUN mv cctv_*_Debug_Linux cctv
|
RUN mv cameradar_*_Debug_Linux cameradar
|
||||||
|
|
||||||
# create cameradaratest folder in go src path
|
RUN mkdir -p /cameradartest/go/src/cameradartest
|
||||||
RUN mkdir -p /go/src/cameradartest
|
COPY src/*.go /cameradartest/go/src/cameradartest/
|
||||||
ADD ./conf /conf
|
COPY ./conf /conf
|
||||||
ADD ./docker/run_cameradartest.sh /run.sh
|
ADD ./docker/run_cameradartest.sh /run.sh
|
||||||
|
|
||||||
# get go deps
|
|
||||||
RUN go get github.com/go-sql-driver/mysql
|
RUN go get github.com/go-sql-driver/mysql
|
||||||
|
|
||||||
RUN mkdir /thumbnails
|
RUN mkdir /thumbnails
|
||||||
WORKDIR /go/src/cameradartest
|
WORKDIR /cameradartest/go/src/cameradartest
|
||||||
|
RUN go build -o cameradartest *.go
|
||||||
CMD ["/run.sh"]
|
CMD ["/run.sh"]
|
||||||
|
|||||||
+16
-8
@@ -1,14 +1,22 @@
|
|||||||
FROM ubuntu:16.04
|
FROM ubuntu:16.04
|
||||||
MAINTAINER brendan.leglaunec@etixgroup.com
|
MAINTAINER brendan.leglaunec@etixgroup.com
|
||||||
|
|
||||||
RUN useradd -m vlc; \
|
RUN apt-get update && apt-get install -y \
|
||||||
apt-get update; \
|
libgstrtspserver-1.0-dev \
|
||||||
apt-get install -y vlc-nox
|
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
|
||||||
|
|
||||||
RUN sed -i s/geteuid/getppid/g /usr/bin/vlc
|
COPY ./docker/run_ces.sh /start.sh
|
||||||
|
COPY ./camera_emulation_server /camera_emulation_server
|
||||||
ADD ./docker/screen.png /vlc/screen.png
|
|
||||||
COPY ./docker/run_vlc.sh /start.sh
|
|
||||||
COPY ./etix_rtsp_server /etix_rtsp_server
|
|
||||||
|
|
||||||
EXPOSE 8554
|
EXPOSE 8554
|
||||||
|
|
||||||
|
RUN ./camera_emulation_server&
|
||||||
|
|||||||
Executable
+55
@@ -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
|
||||||
Executable
BIN
Binary file not shown.
@@ -1,14 +1,16 @@
|
|||||||
{
|
{
|
||||||
"mysql_db" : {
|
"mysql_db" : {
|
||||||
"host" : "0.0.0.0",
|
"host" : "cameradar-database",
|
||||||
"port" : 3306,
|
"port" : 3306,
|
||||||
"user": "root",
|
"user": "root",
|
||||||
"password": "root",
|
"password": "root",
|
||||||
"db_name": "cctv"
|
"db_name": "cmrdr"
|
||||||
},
|
},
|
||||||
"subnets" : "172.16.100.13 localhost",
|
"subnets" : "localhost",
|
||||||
"ports" : "554,8554", // if not specified, default will be 1-65535
|
"ports" : "554,8554",
|
||||||
"rtsp_url_file" : "conf/url.json",
|
"rtsp_url_file" : "/conf/url.json",
|
||||||
"rtsp_ids_file" : "conf/ids.json",
|
"rtsp_ids_file" : "/conf/ids.json",
|
||||||
"thumbnail_storage_path" : "/ce/que/tu/veux"
|
"thumbnail_storage_path" : "/tmp",
|
||||||
|
"cache_manager_path" : "/cameradar/cache_managers",
|
||||||
|
"cache_manager_name" : "dumb"
|
||||||
}
|
}
|
||||||
@@ -2,19 +2,19 @@
|
|||||||
"Output": "cameratest.log.xml",
|
"Output": "cameratest.log.xml",
|
||||||
|
|
||||||
"Cameradar" : {
|
"Cameradar" : {
|
||||||
"Path": "/home/ullaakut/Work/cctv_server2/cameradar/test/cameradar",
|
"Path": "/cameradar/cameradar_standalone/cameradar",
|
||||||
"Args": "-l 1 -c tmp_config",
|
"Args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
|
||||||
"Ports": "554,5554,8554",
|
"Ports": "554,5554,8554",
|
||||||
"IdsPath": "conf/ids.json",
|
"IdsPath": "/conf/ids.json",
|
||||||
"RoutesPath": "conf/url.json",
|
"RoutesPath": "/conf/url.json",
|
||||||
"ThumbPath": "/home/ullaakut/.cctv",
|
"ThumbPath": "/tmp",
|
||||||
"dbHost": "0.0.0.0",
|
"dbHost": "cameradar-database",
|
||||||
"dbPort": 3306,
|
"dbPort": 3306,
|
||||||
"dbUser": "root",
|
"dbUser": "root",
|
||||||
"dbPassword": "root",
|
"dbPassword": "root",
|
||||||
"dbName": "cctv",
|
"dbName": "cmrdr",
|
||||||
"Console": false
|
"Console": false
|
||||||
},
|
},
|
||||||
"Tests" : [
|
"Tests" : [
|
||||||
{
|
{
|
||||||
"address" : "127.0.0.1",
|
"address" : "127.0.0.1",
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"Output": "cameratest.log.xml",
|
"output": "test-results.xml",
|
||||||
"Cameradar" : {
|
|
||||||
"Path": "/cctv/bin/cameradar",
|
"cameradar" : {
|
||||||
"Args": "-l 1 -c tmp_config",
|
"path": "/cameradar/bin/cameradar",
|
||||||
"Ports": "554,5554,8554,5548",
|
"args": "-s 172.17.0.0/24 -c /conf/cameradar.conf.json --gst-rtsp-server",
|
||||||
"IdsPath": "/conf/ids.json",
|
"ports": "554,5554,8554",
|
||||||
"RoutesPath": "/conf/url.json",
|
"ids_path": "conf/ids.json",
|
||||||
"ThumbPath": "/thumbnails",
|
"routes_path": "conf/url.json",
|
||||||
"dbHost": "mysql_cameradar",
|
"thumb_path": "/tmp",
|
||||||
"dbPort": 3306,
|
"db_host": "cameradar-database",
|
||||||
"dbUser": "root",
|
"db_port": 3306,
|
||||||
"dbPassword": "root",
|
"db_user": "root",
|
||||||
"dbName": "cctv",
|
"db_password": "root",
|
||||||
"Console": false
|
"db_name": "cmrdr",
|
||||||
|
"console": false
|
||||||
},
|
},
|
||||||
"Tests" : __CAMERAS__
|
"tests" : __CAMERAS__
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
ports=('8554' '8554' '8554' '8554' '8554' '8554')
|
ports=('8554' '8554' '8554' '8554' '8554' '8554')
|
||||||
users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '')
|
users=('admin' 'root' 'ubnt' 'Admin' 'supervisor' '')
|
||||||
passwords=('admin' 'root' '12345' 'ubnt' 'password' '')
|
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_"
|
cams_name_pattern="fake_camera_"
|
||||||
|
|
||||||
# json generation variable only
|
# json generation variable only
|
||||||
@@ -11,11 +11,16 @@ json="[\n"
|
|||||||
first=true
|
first=true
|
||||||
# $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid
|
# $1 = adress, $2 = port, $3 = path, $4 = usernam $5 = password, $6 = valid
|
||||||
function make_json {
|
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
|
if [ "$first" = true ] ; then first=false
|
||||||
else json="$json,\n"; fi
|
else json="$json,\n"; fi
|
||||||
json="$json{"
|
json="$json{"
|
||||||
json="$json\"address\":\"$1\","
|
json="$json\"address\":\"$address\","
|
||||||
json="$json\"port\":\"$2\","
|
json="$json\"port\":$2,"
|
||||||
json="$json\"route\":\"$3\","
|
json="$json\"route\":\"$3\","
|
||||||
json="$json\"username\":\"$4\","
|
json="$json\"username\":\"$4\","
|
||||||
json="$json\"password\":\"$5\","
|
json="$json\"password\":\"$5\","
|
||||||
@@ -38,7 +43,7 @@ function start {
|
|||||||
for (( i=1; i<=$1; i++ )); do
|
for (( i=1; i<=$1; i++ )); do
|
||||||
name="$cams_name_pattern$i"
|
name="$cams_name_pattern$i"
|
||||||
# random conf
|
# random conf
|
||||||
conf_idx=$(($RANDOM % ${#ports[@]}))
|
conf_idx=$((RANDOM % ${#ports[@]}))
|
||||||
|
|
||||||
# get conf variables
|
# get conf variables
|
||||||
port=${ports[$conf_idx]}
|
port=${ports[$conf_idx]}
|
||||||
@@ -50,18 +55,17 @@ function start {
|
|||||||
# if conf_idx = 4 -> invalid conf
|
# if conf_idx = 4 -> invalid conf
|
||||||
if [ "$conf_idx" == "4" ] ; then is_valid=false; fi
|
if [ "$conf_idx" == "4" ] ; then is_valid=false; fi
|
||||||
|
|
||||||
docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route"
|
CID=$(docker run -d --name "$name" fake-camera /start.sh "$port" "$user" "$passw" "$route");
|
||||||
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid
|
make_json "$name" "$port" "$route" "$user" "$passw" $is_valid $CID
|
||||||
done
|
done
|
||||||
|
|
||||||
# finalize json
|
# finalize json
|
||||||
json="$json]"
|
json="$json]"
|
||||||
echo "$json"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stop {
|
function stop {
|
||||||
# if no cameras containers are started just exit
|
# if no cameras containers are started just exit
|
||||||
camera_count="`docker ps -a -q --filter="name=$cams_name_pattern" | wc -l`"
|
camera_count="$(docker ps -a -q --filter="name=$cams_name_pattern" | wc -l)"
|
||||||
if [ "$camera_count" == "0" ]; then
|
if [ "$camera_count" == "0" ]; then
|
||||||
echo "error: no cameras started"; exit 1
|
echo "error: no cameras started"; exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
while ! mysqladmin ping -h"mysql_cameradar" -P3306 --silent; do
|
while ! mysqladmin ping -h"cameradar-database" -P3306 --silent; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
ls -alhR /conf
|
cat /tmp/tests/cameradartest.conf.json
|
||||||
cat /etc/hosts
|
|
||||||
|
|
||||||
# build
|
# build
|
||||||
go build
|
go build
|
||||||
|
|
||||||
|
cp /tmp/tests/*.xml ./
|
||||||
|
|
||||||
# run test
|
# run test
|
||||||
./cameradartest /tmp/tests/cameradartest.conf.json
|
./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
|
||||||
|
|||||||
Executable
+22
@@ -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}"
|
||||||
@@ -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.
|
Before Width: | Height: | Size: 558 KiB |
Binary file not shown.
-58
@@ -1,58 +0,0 @@
|
|||||||
#!/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
|
|
||||||
(echo "no debug package in the current folder"; exit 137)
|
|
||||||
exit 137
|
|
||||||
fi
|
|
||||||
|
|
||||||
cams_name_pattern="fake_camera_"
|
|
||||||
cmd=""
|
|
||||||
|
|
||||||
function make_docker_command {
|
|
||||||
cmd="docker run --rm"
|
|
||||||
|
|
||||||
# start cameras
|
|
||||||
for (( i=1; i<=$1; i++ )); do
|
|
||||||
name="$cams_name_pattern$i"
|
|
||||||
cmd="$cmd --link=\"$name\""
|
|
||||||
done
|
|
||||||
|
|
||||||
# add mysql libk
|
|
||||||
cmd="$cmd --link=\"mysql_cameradar\""
|
|
||||||
# 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 cameradartest"
|
|
||||||
}
|
|
||||||
|
|
||||||
function start_test {
|
|
||||||
make_docker_command $1
|
|
||||||
./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json
|
|
||||||
eval $cmd
|
|
||||||
./docker/gen_cameras.sh stop
|
|
||||||
}
|
|
||||||
|
|
||||||
# build images
|
|
||||||
echo "building docker images"
|
|
||||||
# building fake-camera container
|
|
||||||
docker build -f Dockerfile-camera -t fake-camera .
|
|
||||||
|
|
||||||
# building cameradartest image
|
|
||||||
docker build -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
|
|
||||||
|
|
||||||
start_test 1
|
|
||||||
start_test 5
|
|
||||||
# start_test 10
|
|
||||||
# start_test 20
|
|
||||||
|
|
||||||
# stop mysql
|
|
||||||
echo "stopping mysql"
|
|
||||||
docker rm -f mysql_cameradar
|
|
||||||
@@ -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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,27 +20,26 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *manager) parseConfig() bool {
|
func (t *Tester) parseConfig() bool {
|
||||||
// Get config file path
|
|
||||||
confPath := "conf/cameratest.conf.json"
|
confPath := "conf/cameratest.conf.json"
|
||||||
av := len(os.Args)
|
av := len(os.Args)
|
||||||
if av == 2 {
|
if av > 1 {
|
||||||
confPath = os.Args[1]
|
confPath = os.Args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
fmt.Printf("Loading config file: %s ... ", confPath)
|
fmt.Printf("Loading Tester configuration file: %s ... ", confPath)
|
||||||
configFile, err := os.Open(confPath)
|
configFile, err := os.Open(confPath)
|
||||||
if err != nil {
|
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
|
return false
|
||||||
}
|
}
|
||||||
dec := json.NewDecoder(configFile)
|
dec := json.NewDecoder(configFile)
|
||||||
if err = dec.Decode(&m); err != nil {
|
if err = dec.Decode(&t); err != nil {
|
||||||
fmt.Printf("\nUnable to deserialize config file: %s\n", err)
|
fmt.Printf("\nUnable to deserialize Tester configuration file: %s\n", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
fmt.Println("Configuration file successfully loaded")
|
fmt.Println("Tester configuration file successfully loaded")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *Tester) dropDB() bool {
|
||||||
|
dsn := t.ServiceConf.DbUser + ":" + t.ServiceConf.DbPassword + "@" + "tcp(" + t.ServiceConf.DbHost + ":" + strconv.Itoa(t.ServiceConf.DbPort) + ")/" + t.ServiceConf.DbName + "?charset=utf8"
|
||||||
|
|
||||||
|
db, err := sql.Open("mysql", dsn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
q := "DROP DATABASE " + t.ServiceConf.DbName + ";"
|
||||||
|
_, err = db.Exec(q)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("------ Dropped Cameradar Database -------")
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -0,0 +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 (
|
||||||
|
"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.Config.Console {
|
||||||
|
fmt.Printf("[%s] %s\n", service.Config.Path, str)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", str)
|
||||||
|
service.Mutex.Lock()
|
||||||
|
service.Logs = append(service.Logs, str)
|
||||||
|
service.Mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := scanner.Err()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[%s] Service failed: %s\n", service.Config.Path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Logger of service: [%s] stopped\n", service.Config.Path)
|
||||||
|
service.Active = false
|
||||||
|
}
|
||||||
+23
-6
@@ -1,30 +1,47 @@
|
|||||||
|
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
manager := new(manager)
|
Tester := new(Tester)
|
||||||
defer manager.Stop()
|
defer Tester.Stop()
|
||||||
|
|
||||||
// Parse conf (streams should already be launched by Jenkins)
|
// Parse conf (streams should already be launched by Jenkins)
|
||||||
fmt.Println("--- Initializing Cameradar Test Tool ... ---")
|
fmt.Println("--- Initializing Cameradar Test Tool ... ---")
|
||||||
if !manager.Init() {
|
if !Tester.Init() {
|
||||||
fmt.Println("-> Cameradar Test Tool initialization FAILED")
|
fmt.Println("-> Cameradar Test Tool initialization FAILED")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run tests
|
// Run tests
|
||||||
if !manager.Run() {
|
if !Tester.Run() {
|
||||||
fmt.Println("-> Cameradar Test Tool FAILED")
|
fmt.Println("-> Cameradar Test Tool FAILED")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write results
|
// Write results
|
||||||
fmt.Println("--- Writing 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")
|
fmt.Println("-> Write results FAILED")
|
||||||
return
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("--- Writing results done ---")
|
fmt.Println("--- Writing results done ---")
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
fmt.Println(e.Address + "seems valid.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend takes a slice of Results and adds a new element to it
|
||||||
|
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
|
||||||
|
}
|
||||||
+46
-29
@@ -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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,32 +21,36 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service needs refacto
|
// ServiceConfig contains the configuration variables for the service structure
|
||||||
type Service struct {
|
type ServiceConfig struct {
|
||||||
Path string `json:"Path"`
|
Path string `json:"path"`
|
||||||
Args string `json:"Args"`
|
Args string `json:"args"`
|
||||||
Ports string `json:"Ports"`
|
Ports string `json:"ports"`
|
||||||
IdsPath string `json:"IdsPath"`
|
IdsPath string `json:"ids_path"`
|
||||||
RoutesPath string `json:"RoutesPath"`
|
RoutesPath string `json:"routes_path"`
|
||||||
DbHost string `json:"dbHost"`
|
ThumbPath string `json:"thumb_path"`
|
||||||
DbPort int `json:"dbPort"`
|
DbHost string `json:"db_host"`
|
||||||
DbUser string `json:"dbUser"`
|
DbPort int `json:"db_port"`
|
||||||
DbPassword string `json:"dbPassword"`
|
DbUser string `json:"db_user"`
|
||||||
DbName string `json:"dbName"`
|
DbPassword string `json:"db_password"`
|
||||||
ThumbPath string `json:"ThumbPath"`
|
DbName string `json:"db_name"`
|
||||||
Console bool `json:"Console"`
|
Console bool `json:"console"`
|
||||||
|
|
||||||
Logs []string
|
|
||||||
Active bool // Based on io.ReadCloser status
|
|
||||||
|
|
||||||
Mutex sync.Mutex
|
|
||||||
cmd *exec.Cmd // Go handler of the service
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startService(service *Service) bool {
|
// Service allows to run a command and to access its logs asynchronously
|
||||||
|
type Service struct {
|
||||||
|
Config ServiceConfig // Configuration variables
|
||||||
|
Logs []string // Contains the executer's logs
|
||||||
|
Active bool // Based on io.ReadCloser status
|
||||||
|
Mutex sync.Mutex // Used to append to the logs safely
|
||||||
|
cmd *exec.Cmd // Pointer to the executer
|
||||||
|
}
|
||||||
|
|
||||||
|
func startService(service *Service, config ServiceConfig) bool {
|
||||||
// Launch service
|
// Launch service
|
||||||
args := strings.Fields(service.Args)
|
service.Config = config
|
||||||
service.cmd = exec.Command(service.Path, args...)
|
args := strings.Fields(service.Config.Args)
|
||||||
|
service.cmd = exec.Command(service.Config.Path, args...)
|
||||||
|
|
||||||
handler, err := service.cmd.StdoutPipe()
|
handler, err := service.cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,7 +69,7 @@ func startService(service *Service) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Service: [%s] started\n", service.Path)
|
fmt.Printf("Service: [%s] started\n", service.Config.Path)
|
||||||
service.Active = true
|
service.Active = true
|
||||||
|
|
||||||
// Read service logs and update service status
|
// Read service logs and update service status
|
||||||
@@ -70,16 +88,15 @@ func stopService(service *Service) {
|
|||||||
// Kill all instances of specified service
|
// Kill all instances of specified service
|
||||||
func killService(service *Service) {
|
func killService(service *Service) {
|
||||||
// Sending SIGTERM
|
// Sending SIGTERM
|
||||||
fmt.Printf("Executing: killall %s\n", service.Path)
|
fmt.Printf("Executing: killall %s\n", service.Config.Path)
|
||||||
cmd := exec.Command("killall", service.Path)
|
cmd := exec.Command("killall", service.Config.Path)
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sending SIGABORT, more reliable for VLC
|
sigAbort := []string{service.Config.Path, "-s", "SIGABRT"}
|
||||||
sigAbort := []string{service.Path, "-s", "SIGABRT"}
|
fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Config.Path)
|
||||||
fmt.Printf("Executing: killall %s -s SIGABRT\n", service.Path)
|
|
||||||
cmd = exec.Command("killall", sigAbort...)
|
cmd = exec.Command("killall", sigAbort...)
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+64
-140
@@ -1,173 +1,97 @@
|
|||||||
|
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MysqlDB needs refacto
|
// Test represents a test launched with Cameradar
|
||||||
type MysqlDB struct {
|
type Test struct {
|
||||||
Host string `json:"host"`
|
expected []Result // Contains the expected results
|
||||||
Port int `json:"port"`
|
result []Result // Contains the results that have been validated
|
||||||
User string `json:"user"`
|
time time.Duration // Contains the runtime duration
|
||||||
Password string `json:"password"`
|
|
||||||
DbName string `json:"db_name"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CameradarConfig needs refacto
|
func removeResult(expected []Result, index int) []Result {
|
||||||
type CameradarConfig struct {
|
if len(expected) > 1 {
|
||||||
MysqlDB MysqlDB `json:"mysql_db"`
|
return append(expected[:index], expected[index+1:]...)
|
||||||
Subnets string `json:"subnets"`
|
}
|
||||||
Ports string `json:"ports"`
|
return []Result{}
|
||||||
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 {
|
|
||||||
expected []Result
|
|
||||||
result []Result
|
|
||||||
time time.Duration
|
|
||||||
ok bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the test
|
// Invoke the test
|
||||||
// Wrap results in a TestResult object
|
// 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()
|
startTime := time.Now()
|
||||||
if m.runTestCase(testCase) {
|
t.runTestCase(testCase)
|
||||||
testCase.time = time.Since(startTime)
|
testCase.time = time.Since(startTime)
|
||||||
testCase.ok = true
|
fmt.Printf("Test OK in %.6fs\n", testCase.time.Seconds())
|
||||||
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())
|
|
||||||
}
|
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) runTestCase(test *TestCase) bool {
|
// Checks all valid results that are supposed to match
|
||||||
fmt.Printf("Test triggered\n")
|
// Adds them to the valid results and leave the failed
|
||||||
|
// ones in the expected slice
|
||||||
Cameradar := m.Config.Cameradar
|
//
|
||||||
startService(&Cameradar)
|
// 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
|
||||||
for Cameradar.Active {
|
func (t *Tester) runTestCase(test *Test) {
|
||||||
time.Sleep(5 * time.Millisecond)
|
startService(&t.Cameradar, t.ServiceConf)
|
||||||
|
for t.Cameradar.Active {
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
}
|
}
|
||||||
found := 0
|
|
||||||
toFind := len(test.expected)
|
|
||||||
var validResults []Result
|
var validResults []Result
|
||||||
if getResult(&test.result, "result.json") {
|
var invalidResults []Result
|
||||||
// Check all valid resutls that are supposed to match
|
if getResult(&test.result, "/tmp/shared/result.json") {
|
||||||
// Add them to the valid results and leave the failed
|
|
||||||
// ones in the expected slice
|
|
||||||
for _, r := range test.result {
|
for _, r := range test.result {
|
||||||
index := 0
|
|
||||||
r.Valid = true
|
r.Valid = true
|
||||||
for _, e := range test.expected {
|
|
||||||
e.Thumb = r.Thumb
|
for index, e := range test.expected {
|
||||||
var err error
|
fmt.Println("Result : ", r)
|
||||||
var addr []string
|
fmt.Println("Expected test : ", e)
|
||||||
addr, err = net.LookupHost(e.Address)
|
|
||||||
e.Address = addr[0]
|
if e.Address == r.Address && isValid(&e, r) {
|
||||||
if e == r {
|
fmt.Println("The result of ", r.Address, " is valid.")
|
||||||
_, err = os.Stat(r.Thumb)
|
validResults = Extend(validResults, r)
|
||||||
if err == nil {
|
test.expected = removeResult(test.expected, index)
|
||||||
fmt.Println("The result of ", r.Address, " is valid and the thumbnails were generated by Cameradar.")
|
break
|
||||||
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.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 {
|
for _, e := range test.expected {
|
||||||
if !e.Valid {
|
if !e.Valid {
|
||||||
found++
|
fmt.Println("The result of", e.Address, "successfully failed.")
|
||||||
validResults = Extend(validResults, e)
|
validResults = Extend(validResults, e)
|
||||||
test.expected = append(test.expected[:index], test.expected[index+1:]...)
|
} else {
|
||||||
break
|
if e.err == nil {
|
||||||
|
e.err = errors.New("The camera with the address " + e.Address + " was not found by cameradar")
|
||||||
|
}
|
||||||
|
invalidResults = Extend(invalidResults, 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
|
test.result = validResults
|
||||||
|
test.expected = invalidResults
|
||||||
|
} else {
|
||||||
|
test.expected = nil
|
||||||
|
test.result = nil
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
ServiceConf ServiceConfig `json:"cameradar"`
|
||||||
|
Output string `json:"output"`
|
||||||
|
Tests []Result `json:"tests"`
|
||||||
|
|
||||||
|
Cameradar Service
|
||||||
|
Result *Test
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
+93
-81
@@ -1,33 +1,48 @@
|
|||||||
|
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
|
||||||
// Data declarations
|
|
||||||
|
|
||||||
// JUnitTestSuites is a collection of JUnit test suites.
|
// JUnitTestSuites is a collection of JUnit test suites.
|
||||||
type JUnitTestSuites struct {
|
type JUnitTestSuites struct {
|
||||||
XMLName xml.Name `xml:"testsuites"`
|
XMLName xml.Name `xml:"testsuites"`
|
||||||
Suites []JUnitTestSuite
|
TestSuites []JUnitTestSuite `xml:"testsuite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JUnitTestSuite is a single JUnit test suite which may contain many
|
// JUnitTestSuite is a single JUnit test suite which may contain many
|
||||||
// testcases.
|
// testcases.
|
||||||
type JUnitTestSuite struct {
|
type JUnitTestSuite struct {
|
||||||
Tests int `xml:"tests,attr"`
|
XMLName xml.Name `xml:"testsuite"`
|
||||||
Failures int `xml:"failures,attr"`
|
Tests int `xml:"tests,attr"`
|
||||||
Time string `xml:"time,attr"`
|
Failures int `xml:"failures,attr"`
|
||||||
TestCases []JUnitTestCase
|
Time string `xml:"time,attr"`
|
||||||
|
TestCases []JUnitTestCase `xml:"testcase"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JUnitTestCase is a single test case with its result.
|
// JUnitTestCase is a single test case with its result.
|
||||||
type JUnitTestCase struct {
|
type JUnitTestCase struct {
|
||||||
|
XMLName xml.Name `xml:"testcase"`
|
||||||
Message string `xml:"message,attr"`
|
Message string `xml:"message,attr"`
|
||||||
Time string `xml:"time,attr"`
|
Time string `xml:"time,attr"`
|
||||||
Failure *JUnitFailure `xml:"failure,omitempty"`
|
Failure *JUnitFailure `xml:"failure,omitempty"`
|
||||||
@@ -35,115 +50,112 @@ type JUnitTestCase struct {
|
|||||||
|
|
||||||
// JUnitFailure contains data related to a failed test.
|
// JUnitFailure contains data related to a failed test.
|
||||||
type JUnitFailure struct {
|
type JUnitFailure struct {
|
||||||
Message string `xml:"message,attr"`
|
XMLName xml.Name `xml:"failure"`
|
||||||
Type string `xml:"type,attr"`
|
Message string `xml:"message,attr"`
|
||||||
Contents string `xml:",chardata"`
|
Type string `xml:"type,attr"`
|
||||||
|
Contents string `xml:",chardata"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) WriteResults(result TestCase, output string) bool {
|
// WriteResults will output the results in the standard output as well as concatenate them in an XML JUnit report
|
||||||
fmt.Printf("Displaying results...\n")
|
func (t *Tester) WriteResults(result Test, output string) bool {
|
||||||
// Write Console report
|
|
||||||
m.writeConsoleReport(result)
|
|
||||||
|
|
||||||
// Write XML report
|
|
||||||
// Open xml
|
|
||||||
file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644)
|
file, err := os.OpenFile(output, os.O_RDONLY|os.O_CREATE, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error opening XML: %s\n", err)
|
fmt.Printf("Error opening XML: %s\n", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
err = m.writeJUnitReportXML(result, file, output)
|
|
||||||
|
err = t.writeJUnitReportXML(result, file, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error writing XML: %s\n", err)
|
fmt.Printf("The tests were unsuccessful: %s\n", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("-> JUnit XML report written: %s\n", output)
|
fmt.Printf("-> JUnit XML report written: %s\n", output)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write tests results under JUnit format on w
|
// 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{}
|
if result.expected == nil && result.result == nil {
|
||||||
dec := xml.NewDecoder(r)
|
return errors.New("Test results could not be deserialized.")
|
||||||
if err := dec.Decode(&suites); err != nil {
|
|
||||||
fmt.Printf("\nUnable to deserialize XML log file: %s\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suites := JUnitTestSuites{}
|
||||||
|
|
||||||
|
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{
|
ts := JUnitTestSuite{
|
||||||
Tests: len(result.result) + len(result.expected),
|
Tests: len(result.result) + len(result.expected),
|
||||||
Failures: 0,
|
Failures: len(result.expected),
|
||||||
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
|
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
|
||||||
TestCases: []JUnitTestCase{},
|
TestCases: []JUnitTestCase{},
|
||||||
}
|
}
|
||||||
// Run throught all iterations
|
|
||||||
testCase := JUnitTestCase{
|
for _, r := range result.result {
|
||||||
Time: fmt.Sprintf("%.6f", result.time.Seconds()),
|
testCase := JUnitTestCase{
|
||||||
Failure: nil,
|
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
|
successCount := 0
|
||||||
}
|
failureCount := 0
|
||||||
if !result.ok {
|
for _, test := range ts.TestCases {
|
||||||
testCase.Failure = &JUnitFailure{
|
if test.Failure != nil {
|
||||||
Message: "These streams did not match what we expected:",
|
failureCount++
|
||||||
Type: "",
|
} else {
|
||||||
|
successCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, fail := range result.expected {
|
|
||||||
ts.Failures++
|
|
||||||
testCase.Failure.Message += " " + fail.Address
|
|
||||||
}
|
|
||||||
ts.TestCases = append(ts.TestCases, testCase)
|
|
||||||
|
|
||||||
suites.Suites = append(suites.Suites, ts)
|
fmt.Println("--- Test summary ---")
|
||||||
// Fix indent
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
suites.TestSuites = append(suites.TestSuites, ts)
|
||||||
bytes, err := xml.MarshalIndent(suites, "", "\t")
|
bytes, err := xml.MarshalIndent(suites, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Write in param stream
|
|
||||||
|
|
||||||
w, err := os.OpenFile(output, os.O_WRONLY|os.O_TRUNC, 0644)
|
w, err := os.OpenFile(output, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
writer := io.Writer(w)
|
writer := io.Writer(w)
|
||||||
writer.Write(bytes)
|
writer.Write(bytes)
|
||||||
|
|
||||||
|
if failureCount > 0 {
|
||||||
|
return errors.New("Some cameras were not successfully accessed.")
|
||||||
|
}
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
Executable
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# check if a debug package exist in the current folder
|
||||||
|
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
|
||||||
|
|
||||||
|
cams_name_pattern="fake_camera_"
|
||||||
|
cmd=""
|
||||||
|
|
||||||
|
function make_docker_command {
|
||||||
|
cmd="docker run --rm"
|
||||||
|
|
||||||
|
# start cameras
|
||||||
|
for (( i=1; i<=$1; i++ )); do
|
||||||
|
name="$cams_name_pattern$i"
|
||||||
|
cmd="$cmd --link=\"$name\""
|
||||||
|
done
|
||||||
|
|
||||||
|
# add mysql link
|
||||||
|
cmd="$cmd --link=\"cameradar-database\""
|
||||||
|
# add cameradar sources
|
||||||
|
cmd="$cmd -v \"$(pwd)/src:/go/src/cameradartest\""
|
||||||
|
# add cameradar testing volume
|
||||||
|
cmd="$cmd -v \"$(pwd)/:/tmp/tests\""
|
||||||
|
# add cameradar shared volume
|
||||||
|
cmd="$cmd -v \"$(pwd)/:/tmp/shared\""
|
||||||
|
# add container name
|
||||||
|
cmd="$cmd cameradartest"
|
||||||
|
}
|
||||||
|
|
||||||
|
function start_test {
|
||||||
|
# Generate all cameras
|
||||||
|
./docker/gen_cameras.sh start $1 ./docker/cameratest.conf.tmpl.json
|
||||||
|
|
||||||
|
# Prepare docker command
|
||||||
|
make_docker_command $1
|
||||||
|
|
||||||
|
# Launch docker command
|
||||||
|
eval $cmd
|
||||||
|
|
||||||
|
# Get its return
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
# Stop all camera containers
|
||||||
|
./docker/gen_cameras.sh stop
|
||||||
|
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "building docker images"
|
||||||
|
|
||||||
|
# building fake-camera container
|
||||||
|
docker build --no-cache -f Dockerfile-camera -t fake-camera .
|
||||||
|
|
||||||
|
# building cameradartest image
|
||||||
|
docker build --no-cache -t cameradartest .
|
||||||
|
|
||||||
|
# getting mysql
|
||||||
|
echo "starting mysql"
|
||||||
|
docker pull mysql:5.7
|
||||||
|
docker run --name cameradar-database -e MYSQL_DATABASE=cmrdr -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7
|
||||||
|
|
||||||
|
start_test 5
|
||||||
|
ret=$?
|
||||||
|
echo "Tests returned ${ret}"
|
||||||
|
|
||||||
|
# stop mysql
|
||||||
|
echo "stopping mysql"
|
||||||
|
docker rm -f cameradar-database
|
||||||
|
exit $ret
|
||||||
Reference in New Issue
Block a user