all demo
This commit is contained in:
35
ansible/playbook/hetzner_app.yml
Normal file
35
ansible/playbook/hetzner_app.yml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Основной playbook-файл для настройки сервера приложений для presale стендов в Hetzner
|
||||||
|
# Автор: Гузаев Павел
|
||||||
|
# Дата создания: 01.02.2022
|
||||||
|
|
||||||
|
- hosts: sd-gate-presale
|
||||||
|
remote_user: root
|
||||||
|
gather_facts: yes
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- hetzner_server
|
||||||
|
|
||||||
|
post_tasks:
|
||||||
|
- name: Add host to group 'sd-presale'
|
||||||
|
add_host:
|
||||||
|
name: "{{ ip_addr }}"
|
||||||
|
groups:
|
||||||
|
- sd-presale
|
||||||
|
|
||||||
|
- hosts: sd-presale
|
||||||
|
remote_user: root
|
||||||
|
gather_facts: yes
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- hetzner_app
|
||||||
|
- role: consul
|
||||||
|
vars:
|
||||||
|
pool: hetzner
|
||||||
|
service: sd-apps,apps,presale
|
||||||
|
machine_type: virtual
|
||||||
|
- role: system_exporter
|
||||||
|
vars:
|
||||||
|
pool: hetzner
|
||||||
|
service: sd-apps,apps,presale
|
||||||
|
machine_type: virtual
|
||||||
|
- ldap-auth
|
||||||
39
ansible/playbook/pg_dump/README.md
Normal file
39
ansible/playbook/pg_dump/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Ansible Database Backup
|
||||||
|
|
||||||
|
[](https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_db_module.html#ansible-collections-community-postgresql-postgresql-db-module)
|
||||||
|
|
||||||
|
Backup database from remote postgresql for {{store}} days.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
ansible-galaxy collection install community.postgresql
|
||||||
|
```
|
||||||
|
## How to dump
|
||||||
|
```
|
||||||
|
ansible-playbook sd_pro_dump.yml -l sd-pgsql9 --extra-vars "dbname=sd4_itsmcorp_devel store=7 arg=--format=custom"
|
||||||
|
```
|
||||||
|
```
|
||||||
|
Avalable env:
|
||||||
|
store: default('1') - how long do you need to store current backup
|
||||||
|
target: default('/opt/back/internal/postgres/') - where place put the archive
|
||||||
|
arg: default('--exclude-table=tbl_event --format=custom') - pg_dump argument
|
||||||
|
```
|
||||||
|
|
||||||
|
#### PostgreSQL
|
||||||
|
|
||||||
|
[Official documentaiton.](https://www.postgresql.org/docs/current/static/app-pgdump.html)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
pg_dump --format "custom" --file "/opt/back/internal/postgres/{{ dbname }}.bak" {{ dbname }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Databases
|
||||||
|
|
||||||
|
For now supports:
|
||||||
|
* PostgreSQL
|
||||||
|
|
||||||
|
## Supported OS
|
||||||
|
Linux
|
||||||
28
ansible/playbook/pg_dump/pg_dump.yml
Normal file
28
ansible/playbook/pg_dump/pg_dump.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
- hosts: all
|
||||||
|
remote_user: ansible
|
||||||
|
vars:
|
||||||
|
store_env: "{{ store|default('1') }}"
|
||||||
|
target_env: "{{ target|default('/opt/back/internal/postgres/') }}"
|
||||||
|
extra_args: "{{ arg|default('--exclude-table=tbl_event --format=custom') }}"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- debug:
|
||||||
|
var: store_env
|
||||||
|
- debug:
|
||||||
|
var: target_env
|
||||||
|
|
||||||
|
- name: Add num_of_days days to start_date
|
||||||
|
command: date +'%Y-%m-%d' -d "+{{store_env}} days"
|
||||||
|
register: end_date
|
||||||
|
- debug:
|
||||||
|
var: end_date.stdout
|
||||||
|
|
||||||
|
- name: Dump the "{{ dbname }}" database to a file
|
||||||
|
become: yes
|
||||||
|
become_method: sudo
|
||||||
|
become_user: postgres
|
||||||
|
community.postgresql.postgresql_db:
|
||||||
|
name: "{{ dbname }}"
|
||||||
|
state: dump
|
||||||
|
target: "{{ target_env }}{{ dbname }}_{{ ansible_date_time.date }}_{{ end_date.stdout }}.bak"
|
||||||
|
dump_extra_args: "{{ extra_args }}"
|
||||||
6
ansible/playbook/roles/gate/README.md
Normal file
6
ansible/playbook/roles/gate/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Получаем данные напрямую из naupp
|
||||||
|
|
||||||
|
url: "https://{{ gate_naupp_fqdn }}/sd/services/rest/exec?accessKey={{ accesskey }}&func=modules.externalAccess.getExternalAccesses¶ms=user"
|
||||||
|
|
||||||
|
На текущий момент роль ставит все необходимые для работы пакеты, настраивает их автозапуск и разрешает проброс пакетов, после чего настраивает файлы для iptables-save и перезапускает службу при необходимости.
|
||||||
|
Для отключения проброса пакетов, например, в случае подозрения на несанкционированный доступ, поменять переменную iptables_ip_forward
|
||||||
5
ansible/playbook/roles/gate/gate.yml
Normal file
5
ansible/playbook/roles/gate/gate.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
- hosts: sd-gw
|
||||||
|
become: yes
|
||||||
|
remote_user: root
|
||||||
|
roles:
|
||||||
|
- gate
|
||||||
15
ansible/playbook/roles/gate/roles/gate/defaults/main.yml
Normal file
15
ansible/playbook/roles/gate/roles/gate/defaults/main.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# Fast disable forward if we have a problem now
|
||||||
|
gate_iptables_ip_forward: 1
|
||||||
|
|
||||||
|
# Name of the service to reload
|
||||||
|
gate_iptables_rules_file: /etc/iptables/rules
|
||||||
|
|
||||||
|
# gate host ext and int ip.
|
||||||
|
gate_local_net: 192.168.0.0/16,10.0.0.0/8
|
||||||
|
gate_int_if: ens18
|
||||||
|
gate_ext_if: ens19
|
||||||
|
|
||||||
|
# naupp site and access_key
|
||||||
|
gate_naupp_fqdn: naupp.nau.com
|
||||||
|
gate_access_key: akfjj666-f897-9978-b5006c-0508938848
|
||||||
6
ansible/playbook/roles/gate/roles/gate/handlers/main.yml
Normal file
6
ansible/playbook/roles/gate/roles/gate/handlers/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: restart netfilter-persistent
|
||||||
|
service:
|
||||||
|
name: netfilter-persistent
|
||||||
|
state: restarted
|
||||||
|
sleep: 10
|
||||||
62
ansible/playbook/roles/gate/roles/gate/tasks/main.yml
Normal file
62
ansible/playbook/roles/gate/roles/gate/tasks/main.yml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
- name: install iptables
|
||||||
|
apt:
|
||||||
|
name: iptables
|
||||||
|
state: present
|
||||||
|
tags: packages
|
||||||
|
|
||||||
|
- name: install iptables-persistent for Debian
|
||||||
|
apt:
|
||||||
|
pkg: iptables-persistent
|
||||||
|
state: present
|
||||||
|
tags: packages
|
||||||
|
|
||||||
|
- name: GET RESULT
|
||||||
|
uri:
|
||||||
|
url: "https://{{ gate_naupp_fqdn }}/sd/services/rest/exec?accessKey={{ gate_access_key }}&func=modules.externalAccess.getExternalAccesses¶ms=user"
|
||||||
|
method: GET
|
||||||
|
return_content: yes
|
||||||
|
delegate_to: 127.0.0.1
|
||||||
|
register: _result
|
||||||
|
until: _result.status != -1
|
||||||
|
retries: 15
|
||||||
|
delay: 15
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
endpoint_naupp: "{{ _result['content'] }}"
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg: "{{ endpoint_naupp }}"
|
||||||
|
|
||||||
|
- name: setup IP forwarding for IPv4
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv4.ip_forward
|
||||||
|
value: "{{ gate_iptables_ip_forward }}"
|
||||||
|
tags: configuration
|
||||||
|
|
||||||
|
- name: start iptables and add to boot runlevel
|
||||||
|
service:
|
||||||
|
name: netfilter-persistent
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: configure rules
|
||||||
|
template:
|
||||||
|
src: rules.iptables.j2
|
||||||
|
dest: "{{ gate_iptables_rules_file }}"
|
||||||
|
notify: restart netfilter-persistent
|
||||||
|
tags: configuration
|
||||||
|
|
||||||
|
- name: symlink IPv4 rules
|
||||||
|
file:
|
||||||
|
src: "{{ gate_iptables_rules_file }}"
|
||||||
|
dest: /etc/iptables/rules.v4
|
||||||
|
state: link
|
||||||
|
tags: configuration
|
||||||
|
|
||||||
|
- name: disable IPv6 on all interfaces
|
||||||
|
sysctl:
|
||||||
|
name: net.ipv6.conf.all.disable_ipv6
|
||||||
|
value: 1
|
||||||
|
tags: configuration
|
||||||
|
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
###############################################################################
|
||||||
|
# The MIT License
|
||||||
|
#
|
||||||
|
# Copyright 2012-2014 Jakub Jirutka <jakub@jirutka.cz>.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Basic iptables/IPv4 template for an ordinary servers
|
||||||
|
#
|
||||||
|
# This file is in iptables-restore format. See the man pages for
|
||||||
|
# iptables-restore(8) and iptables-save(8).
|
||||||
|
#
|
||||||
|
# The following is a set of firewall rules that should be applicable to Linux
|
||||||
|
# servers running within departments. It is intended to provide a useful
|
||||||
|
# starting point from which to devise a comprehensive firewall policy for
|
||||||
|
# a host.
|
||||||
|
#
|
||||||
|
# Parts 1 and 3 of these rules are the same for each host, whilst part 2 can be
|
||||||
|
# populated with rules specific to particular hosts. The optional part 4 is
|
||||||
|
# prepared for a NAT rules, e.g. for port forwarding, redirect, masquerade...
|
||||||
|
#
|
||||||
|
# This template is based on http://jdem.cz/v64a3 from University of Leicester.
|
||||||
|
#
|
||||||
|
# For the newest version go to https://gist.github.com/jirutka/3742890.
|
||||||
|
#
|
||||||
|
# @author Jakub Jirutka <jakub@jirutka.cz>
|
||||||
|
# @version 1.3.1
|
||||||
|
# @date 2014-01-28
|
||||||
|
#
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# 1. COMMON HEADER #
|
||||||
|
# #
|
||||||
|
# This section is a generic header that should be suitable for most hosts. #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
*filter
|
||||||
|
|
||||||
|
# Base policy
|
||||||
|
:INPUT DROP [0:0]
|
||||||
|
:FORWARD DROP [0:0]
|
||||||
|
:OUTPUT ACCEPT [0:0]
|
||||||
|
|
||||||
|
# Don't attempt to firewall internal traffic on the loopback device.
|
||||||
|
-A INPUT -i lo -j ACCEPT
|
||||||
|
|
||||||
|
# Continue connections that are already established or related to an established
|
||||||
|
# connection.
|
||||||
|
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||||
|
|
||||||
|
# Drop non-conforming packets, such as malformed headers, etc.
|
||||||
|
-A INPUT -m conntrack --ctstate INVALID -j DROP
|
||||||
|
|
||||||
|
# Block remote packets claiming to be from a loopback address.
|
||||||
|
-A INPUT -s 127.0.0.0/8 ! -i lo -j DROP
|
||||||
|
|
||||||
|
# Drop all packets that are going to broadcast, multicast or anycast address.
|
||||||
|
-A INPUT -m addrtype --dst-type BROADCAST -j DROP
|
||||||
|
-A INPUT -m addrtype --dst-type MULTICAST -j DROP
|
||||||
|
-A INPUT -m addrtype --dst-type ANYCAST -j DROP
|
||||||
|
-A INPUT -d 224.0.0.0/4 -j DROP
|
||||||
|
|
||||||
|
# Chain for preventing SSH brute-force attacks.
|
||||||
|
# Permits 10 new connections within 5 minutes from a single host then drops
|
||||||
|
# incomming connections from that host. Beyond a burst of 100 connections we
|
||||||
|
# log at up 1 attempt per second to prevent filling of logs.
|
||||||
|
-N SSHBRUTE
|
||||||
|
-A SSHBRUTE -m recent --name SSH --set
|
||||||
|
-A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
|
||||||
|
-A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
|
||||||
|
-A SSHBRUTE -j ACCEPT
|
||||||
|
|
||||||
|
# Chain for preventing ping flooding - up to 6 pings per second from a single
|
||||||
|
# source, again with log limiting. Also prevents us from ICMP REPLY flooding
|
||||||
|
# some victim when replying to ICMP ECHO from a spoofed source.
|
||||||
|
-N ICMPFLOOD
|
||||||
|
-A ICMPFLOOD -m recent --set --name ICMP --rsource
|
||||||
|
-A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
|
||||||
|
-A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
|
||||||
|
-A ICMPFLOOD -j ACCEPT
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# 2. HOST SPECIFIC RULES #
|
||||||
|
# #
|
||||||
|
# This section is a good place to enable your host-specific services. #
|
||||||
|
# ! DO NOT FORGOT TO COPY THESE RULES TO firewall.ip6tables TO ALLOW IPV6 ! #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# Accept HTTP and HTTPS
|
||||||
|
#-A INPUT -p tcp -m multiport --dports 80,443 --syn -m conntrack --ctstate NEW -j ACCEPT
|
||||||
|
|
||||||
|
|
||||||
|
# ACCEPT RULES FROM naupp
|
||||||
|
|
||||||
|
{% for params in endpoint_naupp %}
|
||||||
|
-A INPUT -p tcp -m tcp -s {{ params.source_ip|join(',') }} --dport {{ params.external_port }} -j ACCEPT
|
||||||
|
-A FORWARD -p tcp -m tcp -s {{ params.source_ip|join(',') }} --dport {{ params.target_port }} -j ACCEPT
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# 3. GENERAL RULES #
|
||||||
|
# #
|
||||||
|
# This section contains general rules that should be suitable for most hosts. #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# Accept worldwide access to SSH and use SSHBRUTE chain for preventing
|
||||||
|
# brute-force attacks.
|
||||||
|
-A INPUT -s {{ gate_local_net }} -i {{ gate_int_if }} -j ACCEPT
|
||||||
|
-A FORWARD -s {{ gate_local_net }} -i {{ gate_int_if }} -j ACCEPT
|
||||||
|
|
||||||
|
-A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSHBRUTE
|
||||||
|
|
||||||
|
# Permit useful IMCP packet types.
|
||||||
|
# Note: RFC 792 states that all hosts MUST respond to ICMP ECHO requests.
|
||||||
|
# Blocking these can make diagnosing of even simple faults much more tricky.
|
||||||
|
# Real security lies in locking down and hardening all services, not by hiding.
|
||||||
|
-A INPUT -p icmp --icmp-type 0 -m conntrack --ctstate NEW -j ACCEPT
|
||||||
|
-A INPUT -p icmp --icmp-type 3 -m conntrack --ctstate NEW -j ACCEPT
|
||||||
|
-A INPUT -p icmp --icmp-type 8 -m conntrack --ctstate NEW -j ICMPFLOOD
|
||||||
|
-A INPUT -p icmp --icmp-type 11 -m conntrack --ctstate NEW -j ACCEPT
|
||||||
|
|
||||||
|
# Do not log packets that are going to ports used by SMB
|
||||||
|
# (Samba / Windows Sharing).
|
||||||
|
-A INPUT -p udp -m multiport --dports 135,445 -j DROP
|
||||||
|
-A INPUT -p udp --dport 137:139 -j DROP
|
||||||
|
-A INPUT -p udp --sport 137 --dport 1024:65535 -j DROP
|
||||||
|
-A INPUT -p tcp -m multiport --dports 135,139,445 -j DROP
|
||||||
|
|
||||||
|
# Do not log packets that are going to port used by UPnP protocol.
|
||||||
|
-A INPUT -p udp --dport 1900 -j DROP
|
||||||
|
|
||||||
|
# Do not log late replies from nameservers.
|
||||||
|
-A INPUT -p udp --sport 53 -j DROP
|
||||||
|
|
||||||
|
# Good practise is to explicately reject AUTH traffic so that it fails fast.
|
||||||
|
-A INPUT -p tcp --dport 113 --syn -m conntrack --ctstate NEW -j REJECT --reject-with tcp-reset
|
||||||
|
|
||||||
|
# Prevent DOS by filling log files.
|
||||||
|
-A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "
|
||||||
|
|
||||||
|
COMMIT
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# 4. HOST SPECIFIC NAT RULES #
|
||||||
|
# #
|
||||||
|
# Uncomment this section if you want to use NAT table, e.g. for port #
|
||||||
|
# forwarding, redirect, masquerade... #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
*nat
|
||||||
|
|
||||||
|
# Base policy
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
:POSTROUTING ACCEPT [0:0]
|
||||||
|
:OUTPUT ACCEPT [0:0]
|
||||||
|
|
||||||
|
# Redirect port 21 to local port 2121
|
||||||
|
#-A PREROUTING -i eth0 -p tcp --dport 21 -j REDIRECT --to-port 2121
|
||||||
|
# Forward port 8080 to port 80 on host 192.168.1.10
|
||||||
|
#-A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.10:80
|
||||||
|
|
||||||
|
# NAT rules from naupp
|
||||||
|
{% for params in endpoint_naupp %}
|
||||||
|
-A PREROUTING -i {{ gate_ext_if }} -p tcp --dport {{ params.external_port }} -j DNAT --to-destination {{ params.target_ip }}:{{ params.target_port }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
# mask because not default route
|
||||||
|
-A POSTROUTING -o {{ gate_ext_if }} -j MASQUERADE
|
||||||
|
-A POSTROUTING -o {{ gate_int_if }} -j MASQUERADE
|
||||||
|
|
||||||
|
COMMIT
|
||||||
7
ansible/playbook/roles/hetzner_app/defaults/main.yml
Normal file
7
ansible/playbook/roles/hetzner_app/defaults/main.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
hetzner_app_java_11: jdk-11.0.12+7
|
||||||
|
hetzner_app_java_11_file: OpenJDK11U-jdk_x64_linux_hotspot_11.0.12_7.tar.gz
|
||||||
|
hetzner_app_java_8: jdk8u312-b07
|
||||||
|
hetzner_app_java_8_file: OpenJDK8U-jdk_x64_linux_hotspot_8u312b07.tar.gz
|
||||||
|
hetzner_app_swap_file_path: /swapfile
|
||||||
|
hetzner_app_swap_file_size_gb: 8
|
||||||
|
hetzner_app_stands_user: nausd4
|
||||||
160
ansible/playbook/roles/hetzner_app/tasks/main.yml
Normal file
160
ansible/playbook/roles/hetzner_app/tasks/main.yml
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
---
|
||||||
|
- name: Allow all access from RFC1918 networks to this host
|
||||||
|
community.general.ufw:
|
||||||
|
rule: allow
|
||||||
|
src: '{{ item }}'
|
||||||
|
loop:
|
||||||
|
- 10.0.0.0/8
|
||||||
|
- 172.16.0.0/12
|
||||||
|
- 192.168.0.0/16
|
||||||
|
- 195.151.207.0/24
|
||||||
|
- 37.29.46.132/32
|
||||||
|
- 109.235.215.238/32
|
||||||
|
- 84.47.191.162/32
|
||||||
|
- 77.232.53.10/32
|
||||||
|
- 188.187.118.117/32
|
||||||
|
- 91.234.153.110/32
|
||||||
|
- 195.151.8.25/32
|
||||||
|
- 78.30.223.233/32
|
||||||
|
- 109.237.104.138/32
|
||||||
|
- 84.47.168.163/32
|
||||||
|
- 94.28.29.140/32
|
||||||
|
|
||||||
|
- name: Enable UFW
|
||||||
|
community.general.ufw:
|
||||||
|
state: enabled
|
||||||
|
|
||||||
|
- name: set timezone to Asia/Yekaterinburg
|
||||||
|
community.general.timezone:
|
||||||
|
hwclock: local
|
||||||
|
name: Asia/Yekaterinburg
|
||||||
|
|
||||||
|
- name: Install java_11
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "https://github.com/adoptium/temurin11-binaries/releases/download/{{ hetzner_app_java_11 }}/{{ hetzner_app_java_11_file }}"
|
||||||
|
dest: /opt
|
||||||
|
remote_src: yes
|
||||||
|
|
||||||
|
- name: symlink java_11
|
||||||
|
file:
|
||||||
|
src: "/opt/{{ hetzner_app_java_11 }}/"
|
||||||
|
dest: /opt/openjdk_11
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Install java_8
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "https://github.com/adoptium/temurin8-binaries/releases/download/{{ hetzner_app_java_8 }}/{{ hetzner_app_java_8_file }}"
|
||||||
|
dest: /opt
|
||||||
|
remote_src: yes
|
||||||
|
|
||||||
|
- name: symlink java_8
|
||||||
|
file:
|
||||||
|
src: "/opt/{{ hetzner_app_java_8 }}/"
|
||||||
|
dest: /opt/openjdk_8
|
||||||
|
state: link
|
||||||
|
|
||||||
|
- name: Run the equivalent of "apt-get update" as a separate step
|
||||||
|
apt:
|
||||||
|
update_cache: yes
|
||||||
|
|
||||||
|
- name: Install a list of packages
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- libmime-tools-perl
|
||||||
|
- atop
|
||||||
|
- iotop
|
||||||
|
- less
|
||||||
|
- nano
|
||||||
|
- vim
|
||||||
|
- telnet
|
||||||
|
- dnsutils
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- zip
|
||||||
|
- unzip
|
||||||
|
- tar
|
||||||
|
- rsync
|
||||||
|
- screen
|
||||||
|
- openssl
|
||||||
|
- ldap-utils
|
||||||
|
- fontconfig
|
||||||
|
- htop
|
||||||
|
- mc
|
||||||
|
- ttf-mscorefonts-installer
|
||||||
|
- python-simplejson
|
||||||
|
- software-properties-common
|
||||||
|
- traceroute
|
||||||
|
- file
|
||||||
|
- chrony
|
||||||
|
|
||||||
|
- name: Create swap file
|
||||||
|
command: fallocate -l {{ hetzner_app_swap_file_size_gb }}G {{ hetzner_app_swap_file_path }}
|
||||||
|
creates="{{ hetzner_app_swap_file_path }}"
|
||||||
|
tags:
|
||||||
|
- swap.file.create
|
||||||
|
|
||||||
|
- name: Change swap file permissions
|
||||||
|
file: path="{{ hetzner_app_swap_file_path }}"
|
||||||
|
owner=root
|
||||||
|
group=root
|
||||||
|
mode=0600
|
||||||
|
tags:
|
||||||
|
- swap.file.permissions
|
||||||
|
|
||||||
|
- name: "Check swap file type"
|
||||||
|
command: file {{ hetzner_app_swap_file_path }}
|
||||||
|
register: swapfile
|
||||||
|
tags:
|
||||||
|
- swap.file.mkswap
|
||||||
|
|
||||||
|
- name: Make swap file
|
||||||
|
command: "sudo mkswap {{ hetzner_app_swap_file_path }}"
|
||||||
|
when: swapfile.stdout.find('swap file') == -1
|
||||||
|
tags:
|
||||||
|
- swap.file.mkswap
|
||||||
|
|
||||||
|
- name: Write swap entry in fstab
|
||||||
|
mount: name=none
|
||||||
|
src={{ hetzner_app_swap_file_path }}
|
||||||
|
fstype=swap
|
||||||
|
opts=sw
|
||||||
|
passno=0
|
||||||
|
dump=0
|
||||||
|
state=present
|
||||||
|
tags:
|
||||||
|
- swap.fstab
|
||||||
|
|
||||||
|
- name: Mount swap
|
||||||
|
command: "swapon {{ hetzner_app_swap_file_path }}"
|
||||||
|
when: ansible_swaptotal_mb < 1
|
||||||
|
tags:
|
||||||
|
- swap.file.swapon
|
||||||
|
|
||||||
|
- name: "Add the user {{ hetzner_app_stands_user }} with a bash shell"
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ hetzner_app_stands_user }}"
|
||||||
|
shell: /bin/bash
|
||||||
|
home: "/home/{{ hetzner_app_stands_user }}"
|
||||||
|
create_home: yes
|
||||||
|
generate_ssh_key: yes
|
||||||
|
|
||||||
|
- name: Ansible copy authorized_keys
|
||||||
|
copy:
|
||||||
|
src: /root/.ssh/authorized_keys
|
||||||
|
dest: "/home/{{ hetzner_app_stands_user }}/.ssh/authorized_keys"
|
||||||
|
remote_src: yes
|
||||||
|
|
||||||
|
- name: Change file permissions
|
||||||
|
file: path="/home/{{ hetzner_app_stands_user }}/.ssh/authorized_keys"
|
||||||
|
owner="{{ hetzner_app_stands_user }}"
|
||||||
|
group="{{ hetzner_app_stands_user }}"
|
||||||
|
mode=0600
|
||||||
|
|
||||||
|
- name: Create stands directory
|
||||||
|
file:
|
||||||
|
path: /opt/stands
|
||||||
|
state: directory
|
||||||
|
owner: "{{ hetzner_app_stands_user }}"
|
||||||
|
group: "{{ hetzner_app_stands_user }}"
|
||||||
|
mode: 0775
|
||||||
|
|
||||||
6
ansible/playbook/roles/hetzner_server/defaults/main.yml
Normal file
6
ansible/playbook/roles/hetzner_server/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# NetPlan file for internal network
|
||||||
|
hetzner_server_netplan_int_net: /etc/netplan/01-network-manager.yaml
|
||||||
|
hetzner_server_server_location: hel1-dc2
|
||||||
|
# hetzner_server_server_location: fsn1-dc14
|
||||||
|
# default gateway
|
||||||
|
hetzner_server_gw_ip: 10.106.100.1
|
||||||
6
ansible/playbook/roles/hetzner_server/handlers/main.yml
Normal file
6
ansible/playbook/roles/hetzner_server/handlers/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: netplan apply
|
||||||
|
tags: netplan
|
||||||
|
command: ssh {{ ip_addr }} -oStrictHostKeyChecking=no "netplan apply"
|
||||||
|
async: 1
|
||||||
|
poll: 0
|
||||||
65
ansible/playbook/roles/hetzner_server/tasks/main.yml
Normal file
65
ansible/playbook/roles/hetzner_server/tasks/main.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
- name: GET APPS NUMBER
|
||||||
|
shell: hcloud server list -o columns=name | grep sd-apps[[:digit:]] | sed s/[^0-9]//g
|
||||||
|
register: _nodes
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
nodes: "{{ _nodes.stdout_lines }}"
|
||||||
|
|
||||||
|
- name: GET NEXT NODE NUMBER
|
||||||
|
set_fact: max_node_id="{{ nodes | sort | last }}"
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg: "Next Node id is {{ max_node_id | int + 1 }}"
|
||||||
|
|
||||||
|
- name: "Create new sd-apps server with next number {{ max_node_id | int + 1 }}"
|
||||||
|
shell: hcloud server create --datacenter "{{ hetzner_server_server_location }}" --image ubuntu-20.04 --ssh-key ansible,manager,pguzaev@naumen.ru --network 1127862 --start-after-create --type cpx51 --name "sd-apps{{ max_node_id | int + 1 }}-presale"
|
||||||
|
register: _status
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg: "Status is {{ _status.stdout_lines }}"
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
ext_ip_addr: "{{ _status.stdout_lines[3] }}"
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg: "External ip address is {{ ext_ip_addr.split()[1] }}"
|
||||||
|
|
||||||
|
- name: Wait ssh avalaible
|
||||||
|
wait_for:
|
||||||
|
host: "{{ ext_ip_addr.split()[1] }}"
|
||||||
|
port: "22"
|
||||||
|
state: started # Port should be open
|
||||||
|
delay: 10 # No wait before first check (sec)
|
||||||
|
timeout: 240 # Stop checking after timeout (sec)
|
||||||
|
ignore_errors: no
|
||||||
|
|
||||||
|
- name: GET APPS
|
||||||
|
shell: hcloud server describe -o json "sd-apps{{ max_node_id | int + 1 }}-presale"
|
||||||
|
register: _result
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
private_net: "{{ (_result.stdout | from_json).private_net }}"
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
ip_addr: "{{ private_net[0]['ip'] }}"
|
||||||
|
|
||||||
|
- name: GET APPS
|
||||||
|
shell: hcloud server ssh sd-apps{{ max_node_id | int + 1 }}-presale -oStrictHostKeyChecking=no ifconfig | awk '/{{ ip_addr }}/ {print $1}' RS="\n\n"
|
||||||
|
register: _if_int
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
if_int: "{{ _if_int.stdout }}"
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
msg: "internal iface is {{ if_int }} and has ip adress is {{ ip_addr }}"
|
||||||
|
|
||||||
|
- name: Netplan configure rules
|
||||||
|
template:
|
||||||
|
src: 01-network-manager.yaml.js2
|
||||||
|
dest: "/tmp/sd-apps{{ max_node_id | int + 1 }}-presale.yaml"
|
||||||
|
|
||||||
|
- name: Set Netplan
|
||||||
|
shell: scp -oStrictHostKeyChecking=no "/tmp/sd-apps{{ max_node_id | int + 1 }}-presale.yaml" "{{ ip_addr }}:{{ hetzner_server_netplan_int_net }}"
|
||||||
|
notify:
|
||||||
|
- netplan apply
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# This file is generated from ansible autogeneration scripts
|
||||||
|
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
|
||||||
|
# network: {config: disabled}
|
||||||
|
network:
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
{{ if_int }}
|
||||||
|
dhcp4: true
|
||||||
|
nameservers:
|
||||||
|
addresses:
|
||||||
|
- 192.168.224.7
|
||||||
|
- 192.168.240.7
|
||||||
|
- 91.232.196.12
|
||||||
|
search:
|
||||||
|
- office0.naumen.ru
|
||||||
|
routes:
|
||||||
|
- to: 192.168.0.0/16
|
||||||
|
via: 10.106.100.1
|
||||||
|
- to: 172.16.0.0/16
|
||||||
|
via: 10.106.100.1
|
||||||
|
- to: 10.0.0.0/8
|
||||||
|
via: {{ hetzner_server_gw_ip }}
|
||||||
|
|
||||||
16
ansible/playbook/roles/ldap-auth/defaults/main.yml
Normal file
16
ansible/playbook/roles/ldap-auth/defaults/main.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# ldap
|
||||||
|
ldap_server: "ldap://<some_server>"
|
||||||
|
ldap_port: "389"
|
||||||
|
ldap_base: dc=naumen,dc=ru
|
||||||
|
base_group: ou=groups,dc=naumen,dc=ru
|
||||||
|
base_passwd: ou=users,dc=naumen,dc=ru
|
||||||
|
filter_group: (|(objectClass=posixGroup)(objectClass=groupOfNames))
|
||||||
|
filter_passwd: (&(objectClass=posixAccount)(shadowInactive=0)(memberOf=cn=users,ou=groups,dc=naumen,dc=ru))
|
||||||
|
nss_nested_groups: on
|
||||||
|
reconnect_invalidate: passwd,group,nfsidmap
|
||||||
|
uid: nslcd
|
||||||
|
gid: nslcd
|
||||||
|
ssl_type: start_tls
|
||||||
|
ssl: "no"
|
||||||
|
tls_reqcert: "allow"
|
||||||
0
ansible/playbook/roles/ldap-auth/files/key.pub
Normal file
0
ansible/playbook/roles/ldap-auth/files/key.pub
Normal file
14
ansible/playbook/roles/ldap-auth/files/nsswitch.conf.Debian
Normal file
14
ansible/playbook/roles/ldap-auth/files/nsswitch.conf.Debian
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
passwd: compat ldap
|
||||||
|
group: compat ldap
|
||||||
|
shadow: compat ldap
|
||||||
|
gshadow: files
|
||||||
|
|
||||||
|
hosts: files dns
|
||||||
|
networks: files
|
||||||
|
|
||||||
|
protocols: db files
|
||||||
|
services: db files
|
||||||
|
ethers: db files
|
||||||
|
rpc: db files
|
||||||
|
|
||||||
|
netgroup: nis ldap
|
||||||
23
ansible/playbook/roles/ldap-auth/files/nsswitch.conf.RedHat
Normal file
23
ansible/playbook/roles/ldap-auth/files/nsswitch.conf.RedHat
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
passwd: files sss ldap
|
||||||
|
shadow: files sss ldap
|
||||||
|
group: files sss ldap
|
||||||
|
#initgroups: files
|
||||||
|
|
||||||
|
#hosts: db files nisplus nis dns
|
||||||
|
hosts: files dns
|
||||||
|
|
||||||
|
bootparams: nisplus [NOTFOUND=return] files
|
||||||
|
|
||||||
|
ethers: files
|
||||||
|
netmasks: files
|
||||||
|
networks: files
|
||||||
|
protocols: files
|
||||||
|
rpc: files
|
||||||
|
services: files sss
|
||||||
|
|
||||||
|
netgroup: files sss
|
||||||
|
|
||||||
|
publickey: nisplus
|
||||||
|
|
||||||
|
automount: files
|
||||||
|
aliases: files nisplus
|
||||||
7
ansible/playbook/roles/ldap-auth/files/sudoers
Normal file
7
ansible/playbook/roles/ldap-auth/files/sudoers
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Defaults env_reset
|
||||||
|
Defaults mail_badpass
|
||||||
|
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
|
||||||
|
root ALL=(ALL:ALL) ALL
|
||||||
|
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
|
||||||
|
%<some_group> ALL=(ALL) NOPASSWD: ALL
|
||||||
|
administrator ALL=(root) NOPASSWD: /bin/systemctl * dev_branch*_hornetq*
|
||||||
19
ansible/playbook/roles/ldap-auth/handlers/main.yml
Normal file
19
ansible/playbook/roles/ldap-auth/handlers/main.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
- name: restart nscd
|
||||||
|
service:
|
||||||
|
name: nscd
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: restart nslcd
|
||||||
|
service:
|
||||||
|
name: nslcd
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
|
|
||||||
|
- name: restart sshd
|
||||||
|
service:
|
||||||
|
name: sshd
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
356
ansible/playbook/roles/ldap-auth/tasks/main.yml
Normal file
356
ansible/playbook/roles/ldap-auth/tasks/main.yml
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
---
|
||||||
|
- name: Install ldap package (Debian-like)
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- libpam-ldapd
|
||||||
|
- libnss-ldapd
|
||||||
|
- ldap-utils
|
||||||
|
- nscd
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 36000
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Install ldap package (RedHat-like)
|
||||||
|
yum:
|
||||||
|
name:
|
||||||
|
- pam_ldap
|
||||||
|
- nss-pam-ldapd
|
||||||
|
- openldap-clients
|
||||||
|
- nscd
|
||||||
|
state: present
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Disable SELinux on (RedHat-like)
|
||||||
|
selinux:
|
||||||
|
state: disabled
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Remove dist configs (RedHat-like)
|
||||||
|
file:
|
||||||
|
path: /etc/nslcd.conf
|
||||||
|
state: absent
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Backup dist configs (Debian-like)
|
||||||
|
command: mv /etc/nslcd.conf /etc/nslcd_dist.conf
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Create config files (RedHat-like)
|
||||||
|
file:
|
||||||
|
src: /etc/openldap/ldap.conf
|
||||||
|
dest: /etc/nslcd.conf
|
||||||
|
state: link
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Create config files (Debian-like)
|
||||||
|
file:
|
||||||
|
src: /etc/ldap/ldap.conf
|
||||||
|
dest: /etc/nslcd.conf
|
||||||
|
state: link
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Create ldap.conf file (RedHat-like)
|
||||||
|
template:
|
||||||
|
src: ldap.conf.j2
|
||||||
|
dest: "/etc/openldap/ldap.conf"
|
||||||
|
mode: 0440
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Create ldap.conf file (Debian-like)
|
||||||
|
template:
|
||||||
|
src: ldap.conf.j2
|
||||||
|
dest: "/etc/ldap/ldap.conf"
|
||||||
|
mode: 0440
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Edit nsswitch.conf (RedHat-like)
|
||||||
|
copy:
|
||||||
|
src: "{{ role_path }}/files/nsswitch.conf.RedHat"
|
||||||
|
dest: /etc/nsswitch.conf
|
||||||
|
backup: true
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Edit nsswitch.conf (Debian-like)
|
||||||
|
copy:
|
||||||
|
src: "{{ role_path }}/files/nsswitch.conf.Debian"
|
||||||
|
dest: /etc/nsswitch.conf
|
||||||
|
backup: true
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
notify:
|
||||||
|
- restart nslcd
|
||||||
|
|
||||||
|
- name: Create get_ldap_ssh_key.sh bash script
|
||||||
|
template:
|
||||||
|
src: get_ldap_ssh_key.sh.j2
|
||||||
|
dest: "/usr/bin/get_ldap_ssh_key.sh"
|
||||||
|
mode: 0755
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Update pam.d (Debian-like)
|
||||||
|
pamd:
|
||||||
|
name: common-account
|
||||||
|
type: account
|
||||||
|
control: "required"
|
||||||
|
module_path: pam_permit.so
|
||||||
|
new_type: account
|
||||||
|
new_control: "[success=ok new_authtok_reqd=done ignore=ignore user_unknown=ignore authinfo_unavail=ignore default=bad]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Update pam.d (Debian-like)
|
||||||
|
pamd:
|
||||||
|
name: common-auth
|
||||||
|
type: auth
|
||||||
|
control: "[success=2 default=ignore]"
|
||||||
|
module_path: pam_unix.so
|
||||||
|
new_type: auth
|
||||||
|
new_control: "[success=1 default=ignore]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500 use_first_pass'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Update pam.d (Debian-like)
|
||||||
|
pamd:
|
||||||
|
name: common-password
|
||||||
|
type: password
|
||||||
|
control: "[success=2 default=ignore]"
|
||||||
|
module_path: pam_unix.so
|
||||||
|
new_type: password
|
||||||
|
new_control: "[success=1 default=ignore]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500 use_first_pass'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Update pam.d (Debian-like)
|
||||||
|
pamd:
|
||||||
|
name: common-session
|
||||||
|
type: session
|
||||||
|
control: "required"
|
||||||
|
module_path: pam_unix.so
|
||||||
|
new_type: session
|
||||||
|
new_control: "[success=ok default=ignore]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Update pam.d (Debian-like)
|
||||||
|
pamd:
|
||||||
|
name: common-session
|
||||||
|
type: session
|
||||||
|
control: "required"
|
||||||
|
module_path: pam_permit.so
|
||||||
|
new_type: session
|
||||||
|
new_control: optional
|
||||||
|
new_module_path: pam_mkhomedir.so
|
||||||
|
module_arguments: 'skel=/etc/skel/'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "Debian"
|
||||||
|
|
||||||
|
- name: Update pam.d (RedHat-like)
|
||||||
|
pamd:
|
||||||
|
name: system-auth-ac
|
||||||
|
type: session
|
||||||
|
control: "required"
|
||||||
|
module_path: pam_unix.so
|
||||||
|
new_type: session
|
||||||
|
new_control: "[success=ok default=ignore]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Update pam.d (RedHat-like)
|
||||||
|
pamd:
|
||||||
|
name: system-auth-ac
|
||||||
|
type: session
|
||||||
|
control: "[success=ok default=ignore]"
|
||||||
|
module_path: pam_ldap.so
|
||||||
|
new_type: session
|
||||||
|
new_control: "optional"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Update pam.d (RedHat-like)
|
||||||
|
pamd:
|
||||||
|
name: password-auth-ac
|
||||||
|
type: session
|
||||||
|
control: "required"
|
||||||
|
module_path: pam_unix.so
|
||||||
|
new_type: session
|
||||||
|
new_control: "[success=ok default=ignore]"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
module_arguments: 'minimum_uid=500'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Update pam.d (RedHat-like)
|
||||||
|
pamd:
|
||||||
|
name: password-auth-ac
|
||||||
|
type: session
|
||||||
|
control: "[success=ok default=ignore]"
|
||||||
|
module_path: pam_ldap.so
|
||||||
|
new_type: session
|
||||||
|
new_control: "optional"
|
||||||
|
new_module_path: pam_ldap.so
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Update pam.d (RedHat-like)
|
||||||
|
pamd:
|
||||||
|
name: postlogin-ac
|
||||||
|
type: session
|
||||||
|
control: "optional"
|
||||||
|
module_path: pam_lastlog.so
|
||||||
|
new_type: session
|
||||||
|
new_control: optional
|
||||||
|
new_module_path: pam_mkhomedir.so
|
||||||
|
module_arguments: 'skel=/etc/skel/'
|
||||||
|
state: after
|
||||||
|
when: ansible_os_family == "RedHat"
|
||||||
|
|
||||||
|
- name: Update nscd.conf
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/nscd.conf
|
||||||
|
regexp: "^reload-count"
|
||||||
|
line: 'reload-count unlimited'
|
||||||
|
|
||||||
|
- name: Update nscd.conf
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/nscd.conf
|
||||||
|
regexp: '^positive-time-to-live passwd'
|
||||||
|
line: 'positive-time-to-live passwd 2592000'
|
||||||
|
|
||||||
|
- name: Update nscd.conf
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/nscd.conf
|
||||||
|
regexp: '^positive-time-to-live group'
|
||||||
|
line: "positive-time-to-live passwd 2592000"
|
||||||
|
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-tpivi group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-tpivi ALL=(ALL) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-321 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-321 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-322 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-322 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-323 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-323 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-324 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-324 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-325 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-325 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-326 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-326 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-327 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-327 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-328 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-328 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-329 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-329 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-329 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-353 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sshd_config for AuthorizedKeysCommand
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
line: "AuthorizedKeysCommand /usr/bin/get_ldap_ssh_key.sh"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sshd_config for AuthorizedKeysCommandUser
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
line: "AuthorizedKeysCommandUser nobody"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sshd_config for AuthorizedKeysCommandUser
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
line: "AllowGroups sd-all root administrator postgres mssql oinstall ansible nausd4"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sshd.conf PermitRootLogin
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: "^PermitRootLogin"
|
||||||
|
line: 'PermitRootLogin No'
|
||||||
|
|
||||||
|
- name: Update sshd.conf Match All
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^AuthorizedKeysCommand /usr/bin/get_ldap_ssh_key.sh'
|
||||||
|
line: 'Match All'
|
||||||
|
|
||||||
|
- name: Update sshd.conf PasswordAuthentication no
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^Match All'
|
||||||
|
line: ' PasswordAuthentication no'
|
||||||
|
|
||||||
|
- name: Update sshd.conf Match User administrator,oracle,postgres,mssql,nausd4,ansible
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^ PasswordAuthentication no'
|
||||||
|
line: 'Match User administrator,oracle,postgres,mssql,nausd4,ansible'
|
||||||
|
notify:
|
||||||
|
- restart sshd
|
||||||
|
- restart nscd
|
||||||
|
- restart nslcd
|
||||||
108
ansible/playbook/roles/ldap-auth/tasks/sec_ssh.yml
Normal file
108
ansible/playbook/roles/ldap-auth/tasks/sec_ssh.yml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
- name: sec_ssh start!
|
||||||
|
hosts: ldap-auth
|
||||||
|
become: yes
|
||||||
|
tasks:
|
||||||
|
|
||||||
|
- name: Update sshd.conf PermitRootLogin
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: "^PermitRootLogin"
|
||||||
|
line: 'PermitRootLogin No'
|
||||||
|
tags:
|
||||||
|
- sec_ssh
|
||||||
|
|
||||||
|
- name: Update sshd.conf Match All
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^AuthorizedKeysCommand /usr/bin/get_ldap_ssh_key.sh'
|
||||||
|
line: 'Match All'
|
||||||
|
tags:
|
||||||
|
- sec_ssh
|
||||||
|
|
||||||
|
- name: Update sshd.conf PasswordAuthentication no
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^Match All'
|
||||||
|
line: ' PasswordAuthentication no'
|
||||||
|
tags:
|
||||||
|
- sec_ssh
|
||||||
|
|
||||||
|
- name: Update sshd.conf Match User administrator,oracle,postgres,mssql,nausd4,ansible
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#Match User'
|
||||||
|
insertbefore: '^ PasswordAuthentication no'
|
||||||
|
line: 'Match User administrator,oracle,postgres,mssql,nausd4,ansible'
|
||||||
|
tags:
|
||||||
|
- sec_ssh
|
||||||
|
|
||||||
|
- name: Update sshd.conf AllowGroups
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: "^AllowGroups"
|
||||||
|
line: 'AllowGroups sd-all root administrator postgres mssql oinstall ansible nausd4'
|
||||||
|
tags:
|
||||||
|
- sec_ssh
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-321 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-321 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-322 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-322 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-323 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-323 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-324 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-324 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-325 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-325 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-326 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-326 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-327 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-327 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-328 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-328 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Update sudoes users for sd-devel-329 group
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/sudoers
|
||||||
|
line: "%sd-devel-329 ALL=(administrator,postgres,mssql,oinstall,nausd4) NOPASSWD: ALL"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: restart sshd
|
||||||
|
service:
|
||||||
|
name: sshd
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
29
ansible/playbook/roles/ldap-auth/tasks/sec_test.yml
Normal file
29
ansible/playbook/roles/ldap-auth/tasks/sec_test.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
- hosts: sec
|
||||||
|
become: yes
|
||||||
|
gather_facts: yes
|
||||||
|
vars:
|
||||||
|
user_to_check: administrator
|
||||||
|
tasks:
|
||||||
|
- name: Check if administrator has sudo right
|
||||||
|
shell: sudo -n -l -U administrator 2>&1 | egrep -c -i "not allowed to run sudo|unknown user|неизвестный пользователь|не разрешается"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
ignore_errors: yes
|
||||||
|
register: right
|
||||||
|
|
||||||
|
- name: show administrator sudo right
|
||||||
|
debug: var=right.stdout
|
||||||
|
|
||||||
|
- name: Create temporary backup of /etc/sudoers
|
||||||
|
copy:
|
||||||
|
src: "/etc/sudoers"
|
||||||
|
remote_src: yes
|
||||||
|
dest: "/etc/sudoers_{{ now().strftime('%Y-%m-%d_%H_%M_%S') }}.bak"
|
||||||
|
# register: "sudoers_backup"
|
||||||
|
when: right.stdout == "0"
|
||||||
|
# changed_when: false
|
||||||
|
|
||||||
|
- name: Send sudoers to remote Server
|
||||||
|
copy: src="../files/sudoers" dest=/etc/sudoers
|
||||||
|
when: right.stdout == "0"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
SSH_USER=$1
|
||||||
|
LDAP_URI={{ ldap_server }}:{{ ldap_port }}
|
||||||
|
GROUP_DN={{ base_group }}
|
||||||
|
BASE_DN={{ base_passwd }}
|
||||||
|
|
||||||
|
|
||||||
|
ldapFilter="(&(shadowInactive=0)(uid=${SSH_USER})(memberOf=cn=users,ou=groups,dc=dc1,dc=com)(sshPublicKey=*))"
|
||||||
|
|
||||||
|
# Get "sshPublicKey":
|
||||||
|
KEY=$(ldapsearch -x -LLL -o ldif-wrap=no -H "${LDAP_URI}" -b "${BASE_DN}" "${ldapFilter}" sshPublicKey | \
|
||||||
|
grep sshPublicKey | \
|
||||||
|
perl -MMIME::Base64 -wpe 's/^sshPublicKey(:{1,2}) (.+)$/$1 eq "::" ? decode_base64($2) : $2/e')
|
||||||
|
echo "${KEY}"
|
||||||
|
|
||||||
|
exit 0
|
||||||
7
ansible/playbook/roles/ldap-auth/templates/ldap.conf.j2
Normal file
7
ansible/playbook/roles/ldap-auth/templates/ldap.conf.j2
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
uri {{ ldap_server }}:{{ ldap_port }}/
|
||||||
|
base {{ ldap_base }}
|
||||||
|
base group {{ base_group }}
|
||||||
|
base passwd {{ base_passwd }}
|
||||||
|
filter group {{ filter_group }}
|
||||||
|
filter passwd {{ filter_passwd }}
|
||||||
|
tls_reqcert {{ tls_reqcert }}
|
||||||
47
compose/ncc-compose/README.md
Normal file
47
compose/ncc-compose/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
## Требования
|
||||||
|
- Установленый docker
|
||||||
|
[Инструкция по установке](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-docker-ce)
|
||||||
|
- Установленый docker-compose => 1.19.0
|
||||||
|
[Инструкция по установке](https://docs.docker.com/compose/install/#install-compose)
|
||||||
|
- Авторизация в реестре sd-docker-registry2.naumen.ru
|
||||||
|
- Авторизация в реестре ncc-75.nau.team
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker login -u user -p superpaassword sd-docker-registry2.naumen.ru
|
||||||
|
docker login -u user -p superpaassword ncc-75.nau.team
|
||||||
|
```
|
||||||
|
|
||||||
|
### В папке createdb script для создания пустых БД необходимых для работы:
|
||||||
|
|
||||||
|
[Инструкция по установке и базовой настройке пакета](https://start.nau.im/pages/viewpage.action?pageId=50773357)
|
||||||
|
|
||||||
|
### Развертывание и запуск приложения осуществляется docker-compose up -d
|
||||||
|
Перед запуском:
|
||||||
|
1.
|
||||||
|
!!! НЕОБХОДИМО проверить что порты на рабочем копьютере свободны, например так:
|
||||||
|
netstat -nat | grep 8081 (для HOST_PORT)
|
||||||
|
|
||||||
|
Прокинутые порты сейчас:
|
||||||
|
- Консул
|
||||||
|
- "8301:8301"
|
||||||
|
- "8400:8400"
|
||||||
|
- "8500:8500"
|
||||||
|
- "8600:8600/udp"
|
||||||
|
- naucore
|
||||||
|
- "3242:3242"
|
||||||
|
|
||||||
|
- chat
|
||||||
|
- "8081:8081"
|
||||||
|
- "8444:8444"
|
||||||
|
- "9000:9000"
|
||||||
|
|
||||||
|
Если порт занят, его необходимо поменять на любой свободный, в противном случае, приложение не запустится.
|
||||||
|
|
||||||
|
2.
|
||||||
|
Убедиться в наличии доступа до сети naumen (ВПН поднят, ресурсы доступны) - все данные для работы будут выкачиваться оттуда.
|
||||||
|
|
||||||
|
### Запускаем приложение
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
После сборки и запуска ( если не меняли порты) веб чата доступен по ссылке:
|
||||||
|
http://localhost:8081/workplace.html#/login
|
||||||
5
compose/ncc-compose/createdb/create.sh
Executable file
5
compose/ncc-compose/createdb/create.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
psql -c "create user ncc_system encrypted password 'ncc_system';"
|
||||||
|
psql -c "create database ncc_system owner ncc_system encoding 'utf-8';"
|
||||||
|
psql -c "create database chat owner ncc_system encoding 'utf-8';"
|
||||||
|
psql -c "create database ncc_report owner ncc_system encoding 'utf-8';"
|
||||||
217
compose/ncc-compose/docker-compose.yaml
Normal file
217
compose/ncc-compose/docker-compose.yaml
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
version: '2.3'
|
||||||
|
services:
|
||||||
|
|
||||||
|
redis-naumb:
|
||||||
|
image: ncc-75.nau.team/redis:5.0.6
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: sd-docker-registry2.naumen.ru/library/postgres:12RU
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
- POSTGRES_MULTIPLE_DATABASES=ncc_system,chat,ncc_report
|
||||||
|
volumes:
|
||||||
|
- ./dbdata:/var/lib/postgresql/data/
|
||||||
|
- ./createdb:/docker-entrypoint-initdb.d
|
||||||
|
healthcheck:
|
||||||
|
test: "pg_isready -q -U ncc_system -d ncc_system && ! pidof pg_restore"
|
||||||
|
interval: 30s
|
||||||
|
start_period: 5m
|
||||||
|
stop_grace_period: 15s
|
||||||
|
|
||||||
|
consul-server:
|
||||||
|
image: "ncc-75.nau.team/consul:1.6"
|
||||||
|
environment:
|
||||||
|
CONSUL_LOCAL_CONFIG: "{\"disable_update_check\": true}"
|
||||||
|
CONSUL_BIND_INTERFACE: "eth0"
|
||||||
|
hostname: "consul-server"
|
||||||
|
ports:
|
||||||
|
- "${CONSUL_PORT:-8500}:8500"
|
||||||
|
volumes:
|
||||||
|
- "./consul-data:/consul/data"
|
||||||
|
command: agent -server -data-dir="/consul/data" -bootstrap -domain="example.com" -client="0.0.0.0" -advertise="127.0.0.1" -ui
|
||||||
|
|
||||||
|
naucore:
|
||||||
|
image: ncc-75.nau.team/naucore:7.5
|
||||||
|
ports:
|
||||||
|
- "${NCC_PORT:-3242}:3242"
|
||||||
|
environment:
|
||||||
|
HOSTNAME: "naucore"
|
||||||
|
domain_id: "ncc.net"
|
||||||
|
node_id: "naucore"
|
||||||
|
CONSUL_SERVER: "consul-server:8500"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
links:
|
||||||
|
- consul-server
|
||||||
|
|
||||||
|
nauauth:
|
||||||
|
image: ncc-75.nau.team/nauauth:7.5
|
||||||
|
tty: yes
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
links:
|
||||||
|
- consul-server
|
||||||
|
- naucore
|
||||||
|
|
||||||
|
naubuddy:
|
||||||
|
image: ncc-75.nau.team/naubuddy:7.5
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
systemdb.engine: "postgresql"
|
||||||
|
systemdb.host: "postgres"
|
||||||
|
systemdb.port: '5432'
|
||||||
|
systemdb.database: "ncc_system"
|
||||||
|
systemdb.user: "ncc_system"
|
||||||
|
systemdb.password: "ncc_system"
|
||||||
|
reportdb.engine: "postgresql"
|
||||||
|
reportdb.host: "postgres"
|
||||||
|
reportdb.port: '5432'
|
||||||
|
reportdb.database: "ncc_report"
|
||||||
|
reportdb.user: "ncc_system"
|
||||||
|
reportdb.password: "ncc_system"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
restart: always
|
||||||
|
tty: yes
|
||||||
|
volumes:
|
||||||
|
- "./spool/naubuddy:/opt/naumen/nauphone/spool/naubuddy"
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
nauconfig:
|
||||||
|
image: ncc-75.nau.team/nauconfig:7.5
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
systemdb.engine: "postgresql"
|
||||||
|
systemdb.host: "postgres"
|
||||||
|
systemdb.port: '5432'
|
||||||
|
systemdb.database: "ncc_system"
|
||||||
|
systemdb.user: "ncc_system"
|
||||||
|
systemdb.password: "ncc_system"
|
||||||
|
reportdb.engine: "postgresql"
|
||||||
|
reportdb.host: "postgres"
|
||||||
|
reportdb.port: '5432'
|
||||||
|
reportdb.database: "ncc_report"
|
||||||
|
reportdb.user: "ncc_system"
|
||||||
|
reportdb.password: "ncc_system"
|
||||||
|
useBuddyListProxy: "true"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
restart: always
|
||||||
|
tty: yes
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
naufileservice:
|
||||||
|
image: ncc-75.nau.team/naufileservice:7.5
|
||||||
|
ports:
|
||||||
|
- "${NCCF_PORT:-8088}:8088"
|
||||||
|
environment:
|
||||||
|
CONSUL_SERVER: "consul-server:8500"
|
||||||
|
bus.ip: "naucore"
|
||||||
|
ncc_ip: "naucore"
|
||||||
|
log_level: "4"
|
||||||
|
tty: yes
|
||||||
|
volumes:
|
||||||
|
- "./spool/recconv:/opt/naumen/nauphone/spool/naurecconv"
|
||||||
|
- "./spool/naubuddy:/opt/naumen/nauphone/spool/naubuddy"
|
||||||
|
- "./spool/naumb:/opt/naumen/nauphone/spool/naumb"
|
||||||
|
- "./spool/nauqpm:/opt/naumen/nauphone/spool/nauqpm"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
naumb:
|
||||||
|
image: ncc-75.nau.team/naumb:7.5
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
redis.host: "redis-naumb"
|
||||||
|
systemdb.engine: "postgresql"
|
||||||
|
systemdb.host: "postgres"
|
||||||
|
systemdb.port: '5432'
|
||||||
|
systemdb.database: "ncc_system"
|
||||||
|
systemdb.user: "ncc_system"
|
||||||
|
systemdb.password: "ncc_system"
|
||||||
|
reportdb.engine: "postgresql"
|
||||||
|
reportdb.host: "postgres"
|
||||||
|
reportdb.port: '5432'
|
||||||
|
reportdb.database: "ncc_report"
|
||||||
|
reportdb.user: "ncc_system"
|
||||||
|
reportdb.password: "ncc_system"
|
||||||
|
gateways.service: "nausd"
|
||||||
|
NCC_LOG_TYPE: "raw"
|
||||||
|
volumes:
|
||||||
|
- "./spool/naubuddy:/opt/naumen/nauphone/spool/naubuddy"
|
||||||
|
- "./spool/naumb:/opt/naumen/nauphone/spool/naumb"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
- redis-naumb
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
naumbstat:
|
||||||
|
image: ncc-75.nau.team/naumbstat:7.5
|
||||||
|
tty: yes
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
redis.host: "redis-naumb"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
- redis-naumb
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
nauqpm:
|
||||||
|
image: ncc-75.nau.team/nauqpm:7.5
|
||||||
|
volumes:
|
||||||
|
- "./spool/nauqpm:/opt/naumen/nauphone/spool/nauqpm"
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
chat:
|
||||||
|
image: ncc-75.nau.team/chat:7.5
|
||||||
|
ports:
|
||||||
|
- "${CHAT_PORT:-8081}:8081"
|
||||||
|
- "${WS_PORT:-9000}:9000"
|
||||||
|
environment:
|
||||||
|
bus.ip: "naucore"
|
||||||
|
chatdb.engine: postgresql
|
||||||
|
chatdb.host: postgres
|
||||||
|
chatdb.port: '5432'
|
||||||
|
chatdb.database: chat
|
||||||
|
chatdb.user: "ncc_system"
|
||||||
|
chatdb.password: "ncc_system"
|
||||||
|
links:
|
||||||
|
- naucore
|
||||||
|
- postgres
|
||||||
|
restart: always
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
18
compose/themes_generator/Dockerfile
Normal file
18
compose/themes_generator/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM openjdk:11
|
||||||
|
COPY --from=python:3.7 / /
|
||||||
|
|
||||||
|
WORKDIR /app/
|
||||||
|
ADD ./app /app/
|
||||||
|
|
||||||
|
RUN set -ex \
|
||||||
|
&& pip3 install --no-cache-dir -r /app/requirements.txt \
|
||||||
|
&& chmod +x /app/mallet/bin/* \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt install ant -y \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
|
||||||
|
&& cd /app/mallet/ \
|
||||||
|
&& ant
|
||||||
|
|
||||||
|
ENTRYPOINT ["python3", "clustering_server.py"]
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
51
compose/themes_generator/requirements.txt
Normal file
51
compose/themes_generator/requirements.txt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
beautifulsoup4==4.8.2
|
||||||
|
boto3==1.12.32
|
||||||
|
botocore==1.15.32
|
||||||
|
bottle==0.12.18
|
||||||
|
cachetools==4.0.0
|
||||||
|
certifi==2019.11.28
|
||||||
|
chardet==3.0.4
|
||||||
|
cheroot==8.3.0
|
||||||
|
CherryPy==8.9.1
|
||||||
|
docutils==0.15.2
|
||||||
|
gensim==3.8.1
|
||||||
|
google-api-core==1.16.0
|
||||||
|
google-auth==1.12.0
|
||||||
|
google-cloud-core==1.3.0
|
||||||
|
google-cloud-storage==1.26.0
|
||||||
|
google-resumable-media==0.5.0
|
||||||
|
googleapis-common-protos==1.51.0
|
||||||
|
idna==2.9
|
||||||
|
importlib-metadata==1.6.0
|
||||||
|
importlib-resources==1.4.0
|
||||||
|
jaraco.classes==3.1.0
|
||||||
|
jaraco.collections==3.0.0
|
||||||
|
jaraco.functools==3.0.0
|
||||||
|
jaraco.text==3.2.0
|
||||||
|
jmespath==0.9.5
|
||||||
|
lxml==4.5.0
|
||||||
|
more-itertools==8.2.0
|
||||||
|
nltk==3.4.5
|
||||||
|
numexpr==2.7.1
|
||||||
|
numpy==1.18.2
|
||||||
|
pandas==1.0.3
|
||||||
|
portend==2.6
|
||||||
|
protobuf==3.11.3
|
||||||
|
psycopg2-binary==2.8.4
|
||||||
|
pyasn1==0.4.8
|
||||||
|
pyasn1-modules==0.2.8
|
||||||
|
pymystem3==0.2.0
|
||||||
|
python-dateutil==2.8.1
|
||||||
|
pytz==2019.3
|
||||||
|
requests==2.23.0
|
||||||
|
rsa==4.0
|
||||||
|
s3transfer==0.3.3
|
||||||
|
scipy==1.4.1
|
||||||
|
six==1.14.0
|
||||||
|
smart-open==1.10.0
|
||||||
|
soupsieve==2.0
|
||||||
|
tables==3.6.1
|
||||||
|
tempora==3.0.0
|
||||||
|
urllib3==1.25.8
|
||||||
|
zc.lockfile==2.0
|
||||||
|
zipp==3.1.0
|
||||||
235
jenkins/1cdb.groovy
Normal file
235
jenkins/1cdb.groovy
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
@Field List props = []
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['DAY','WEEK'],
|
||||||
|
description: 'Выбор бэкапа'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 360, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CURRENT_DATE = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
triggers{
|
||||||
|
parameterizedCron('''
|
||||||
|
30 19 * * 1,2,3,4 %ACTION=DAY
|
||||||
|
30 19 * * 5 %ACTION=WEEK
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Prepare_MASTER') {
|
||||||
|
agent {label "master"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Prepare_SITE') {
|
||||||
|
agent {label "net"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////
|
||||||
|
stage('1CDB') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'DAY'; environment name: 'ACTION', value: 'WEEK'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./site/1cdb_session.ps1 1CDB"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: '1C backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////
|
||||||
|
|
||||||
|
stage('Move_day') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'MOVE'; environment name: 'ACTION', value: 'DAY'}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
mv /srv/SITE/1CDB*.7z /srv/SITE/1CDB/
|
||||||
|
find /srv/SITE/1CDB/ -name "*.7z" -mtime +10 -exec rm -f {} \\;
|
||||||
|
'''
|
||||||
|
telegramSend(message: 'Day 1CDB MOVE success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
stage('Move_week') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'MOVE'; environment name: 'ACTION', value: 'WEEK'}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
mv /srv/SITE/1CDB*.7z /srv/1CSRV/1CDB/
|
||||||
|
find /srv/1CSRV/1CDB/ -name "*.7z" -mtime +60 -exec rm -f {} \\;
|
||||||
|
'''
|
||||||
|
telegramSend(message: 'WEEK 1CDB MOVE success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'success', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Бэкап 1С завершен успешно', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aborted {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'aborted', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Бэкап 1С пришлось остановить', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'failure', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Бэкап 1С завершился с ошибкой', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDesc(ACTION)
|
||||||
|
{
|
||||||
|
def d = [ACTION: '', BACKUP_DATE: '', END_TIME: '']
|
||||||
|
END_TIME = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
d.ACTION = "${ACTION}"
|
||||||
|
d.BACKUP_DATE = "${CURRENT_DATE}"
|
||||||
|
d.END_TIME = "${END_TIME}"
|
||||||
|
d1 = d.clone()
|
||||||
|
props << d1
|
||||||
|
println d1
|
||||||
|
d.clear()
|
||||||
|
println props
|
||||||
|
println d
|
||||||
|
println "${END_TIME}"
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendME(ACTION,STATUS)
|
||||||
|
{
|
||||||
|
emailext body: "${STATUS}",
|
||||||
|
subject: "${ACTION}",
|
||||||
|
to: 'therion@nasda.ru'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: '*/master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs: [[url: 'git@gitlab.domen:user/sys.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def scriptDir = getClass().protectionDomain.codeSource.location.path
|
||||||
|
println "${scriptDir}"
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath()
|
||||||
|
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача бэкапирует сетевые диски</b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, звонить :)
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.backup_table { margin:15px 0; border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 100%;}
|
||||||
|
.backup_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.backup_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.backup_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.backup_table .stand:hover { background:#f6faf2; }
|
||||||
|
.backup_table .col_title { width:120px; }
|
||||||
|
</style>
|
||||||
|
<table class="backup_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Задания бэкапа:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="ACTION">Бэкапирование</th>
|
||||||
|
<th abbr="BACKUP_DATE">Дата выполнения</th>
|
||||||
|
<th abbr="END_TIME">Время завершения</th>
|
||||||
|
</tr>
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="mail">
|
||||||
|
<td abbr="ACTION">${prop.ACTION}</td>
|
||||||
|
<td abbr="BACKUP_DATE">${prop.BACKUP_DATE}</td>
|
||||||
|
<td abbr="END_TIME">${prop.END_TIME}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (DAY, WEEK)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>DAY</b> - Ежедневный бэкап 1С, автоматически делается в 19:30 с понедельника по четверг, архивы в C:\\Backup\\1CDB на сервере SITE
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>WEEK</b> - Еженедельный бэкап 1С, автоматически делается в 19:30 в пятницу, архивы в C:\\Backup\\1CDB на сервере 1CSRV
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
10
jenkins/1cdb_session.ps1
Normal file
10
jenkins/1cdb_session.ps1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
$Action=$args[0]
|
||||||
|
Write-Host $Action
|
||||||
|
|
||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ('user', (ConvertTo-SecureString 'passwd' -AsPlainText -Force))
|
||||||
|
|
||||||
|
Invoke-Command -ComputerName 192.16.0.1 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Remove-Item "D:\SRV\1CDB\*.bak"}
|
||||||
|
Invoke-Command -ComputerName 192.16.0.1 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Backup-SqlDatabase -ServerInstance "." -Database "KD" -BackupFile "D:\SRV\1CDB\KD.bak"}
|
||||||
|
Invoke-Command -ComputerName 192.16.0.1 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Backup-SqlDatabase -ServerInstance "." -Database "IP" -BackupFile "D:\SRV\1CDB\IP.bak"}
|
||||||
|
|
||||||
|
Invoke-Command -ComputerName 192.16.0.1 -Credential $Cred -Authentication Negotiate -ScriptBlock {..\.jenkins\workspace\1cdb_backup\site\site_backup.ps1 $Using:Action}
|
||||||
218
jenkins/1csrv.groovy
Normal file
218
jenkins/1csrv.groovy
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
@Field List props = []
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['ALL','1CDB','KD'],
|
||||||
|
description: 'Выбор бэкапа'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 2400, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CURRENT_DATE = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
triggers{
|
||||||
|
parameterizedCron('''
|
||||||
|
00 23 * * 5 %ACTION=KD
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Prepare_MASTER') {
|
||||||
|
agent {label "master"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Prepare_1CSRV') {
|
||||||
|
agent {label "mssql"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////
|
||||||
|
stage('Backup_KD') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'KD'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./1csrv/1csrv_session.ps1 KD $env.JOB_NAME"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: 'KD backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////
|
||||||
|
|
||||||
|
stage('Backup_1C_MSSQL') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: '1CDB'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./1csrv/1csrv_session.ps1 1CDB $env.JOB_NAME"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: '1C backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'success', extraMessage: 'MSSQL и Сетевые диски успешно сбэкапированы, можно забирать', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aborted {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'aborted', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Пришлось остановить', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'failure', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Бэкап сетевых дисков завершился с ошибкой', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDesc(ACTION)
|
||||||
|
{
|
||||||
|
def d = [ACTION: '', BACKUP_DATE: '', END_TIME: '']
|
||||||
|
END_TIME = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
d.ACTION = "${ACTION}"
|
||||||
|
d.BACKUP_DATE = "${CURRENT_DATE}"
|
||||||
|
d.END_TIME = "${END_TIME}"
|
||||||
|
d1 = d.clone()
|
||||||
|
props << d1
|
||||||
|
println d1
|
||||||
|
d.clear()
|
||||||
|
println props
|
||||||
|
println d
|
||||||
|
println "${END_TIME}"
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendME(ACTION,STATUS)
|
||||||
|
{
|
||||||
|
emailext body: "${STATUS}",
|
||||||
|
subject: "${ACTION}",
|
||||||
|
to: 'therion@nasda.ru'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: '*/master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs: [[url: 'git@gitlab.domen:user/sys.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def scriptDir = getClass().protectionDomain.codeSource.location.path
|
||||||
|
println "${scriptDir}"
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath()
|
||||||
|
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача бэкапирует базы данных 1С</b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, звонить :)
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.backup_table { margin:15px 0; border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 100%;}
|
||||||
|
.backup_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.backup_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.backup_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.backup_table .stand:hover { background:#f6faf2; }
|
||||||
|
.backup_table .col_title { width:120px; }
|
||||||
|
</style>
|
||||||
|
<table class="backup_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Задания бэкапа:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="ACTION">Бэкапирование</th>
|
||||||
|
<th abbr="BACKUP_DATE">Дата выполнения</th>
|
||||||
|
<th abbr="END_TIME">Время завершения</th>
|
||||||
|
</tr>
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="mail">
|
||||||
|
<td abbr="ACTION">${prop.ACTION}</td>
|
||||||
|
<td abbr="BACKUP_DATE">${prop.BACKUP_DATE}</td>
|
||||||
|
<td abbr="END_TIME">${prop.END_TIME}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (ALL, 1CDB, WORK)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ALL</b> - Выполнение всех задач, планировщик делает по умолчанию в выходной
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>1CDB</b> - Бэкап Баз Данных 1С, после выполнения, архив доступен для скачки по ссылке
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>KD</b> - Бэкап Конструкторской документации
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Бэкапы тут</b> - <a href="\\\\1CSRV.domen\\Backup">\\\\1CSRV.domen\\Backup</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
20
jenkins/1csrv_session.ps1
Normal file
20
jenkins/1csrv_session.ps1
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
$Action=$args[0]
|
||||||
|
$Job=$args[1]
|
||||||
|
Write-Host $Action
|
||||||
|
Write-Host $Job
|
||||||
|
|
||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ('user', (ConvertTo-SecureString 'password' -AsPlainText -Force))
|
||||||
|
|
||||||
|
if ( $Action -match '1CDB')
|
||||||
|
{
|
||||||
|
Invoke-Command -ComputerName 192.16.0.6 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Remove-Item "D:\1CDB\*.bak"}
|
||||||
|
Invoke-Command -ComputerName 192.16.0.6 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Backup-SqlDatabase -ServerInstance "." -Database "KD" -BackupFile "D:\1CDB\KD.bak"}
|
||||||
|
Invoke-Command -ComputerName 192.16.0.6 -Credential $Cred -Verbose -Authentication Negotiate -ScriptBlock {Backup-SqlDatabase -ServerInstance "." -Database "IP" -BackupFile "D:\1CDB\IP.bak"}
|
||||||
|
Invoke-Command -ComputerName 192.16.0.6 -Credential $Cred -Authentication Negotiate -ScriptBlock {..\.jenkins\workspace\1csrv_backup\1csrv\1csrv_backup.ps1 $Using:Action}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $Action -match 'KD')
|
||||||
|
{
|
||||||
|
Invoke-Command -ComputerName 172.16.0.6 -Credential $Cred -Authentication Negotiate -ScriptBlock {..\.jenkins\workspace\1csrv_backup\1csrv\1csrv_backup.ps1 $Using:Action}
|
||||||
|
}
|
||||||
|
|
||||||
204
jenkins/astarot.groovy
Normal file
204
jenkins/astarot.groovy
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
@Field List props = []
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['ALL','WEB','LINUX'],
|
||||||
|
description: 'Выбор бэкапа'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 2400, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CURRENT_DATE = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
triggers{
|
||||||
|
parameterizedCron('''
|
||||||
|
00 21 * * 5 %ACTION=WEB
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Prepare_ASTAROT') {
|
||||||
|
agent {label "site"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stage('WEB_BACKUP') {
|
||||||
|
agent {label "site"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'WEB'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
rsync -avHAXxz --progress --delete --numeric-ids --exclude devpribor.ru/core/cache/* -e ssh intertest@188.120.241.91://var/www/intpribor/www /srv/archive/site/dev/
|
||||||
|
rsync -avHAXxz --progress --delete --numeric-ids --exclude interpribor.ru/core/cache/* -e ssh intertest@188.120.241.91://var/www/devtest/www /srv/archive/site/test/
|
||||||
|
rsync -avHAXxz --progress --delete --numeric-ids --exclude interpribor.ru/core/cache/* -e ssh interbase@82.146.55.174://var/www/intpribor/www /srv/archive/site/prod/
|
||||||
|
ssh interbase@82.146.55.174 "mysqldump --host 127.0.0.1 --port 3307 --column-statistics=0 --no-tablespaces -ucarpribor -p1L5n0E7Ah8A9 carpribor | gzip -9" > /srv/archive/site/prod/carpribor_$(date +%d%m%Y).sql.gz
|
||||||
|
tar -zcvf /mnt/backup/site/site_prod_$(date +%d%m%Y).tar.gz /srv/archive/site/prod/
|
||||||
|
tar -zcvf /mnt/backup/site/site_dev_$(date +%d%m%Y).tar.gz /srv/archive/site/dev/www/devpribor.ru/
|
||||||
|
find /mnt/backup/site/ -name "*.gz" -mtime +27 -exec rm -f {} \\;
|
||||||
|
'''
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","WEB Backup success!!!")
|
||||||
|
telegramSend(message: 'WEB BACKUP success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
node ("site") {
|
||||||
|
jabberNotify notificationStrategy: 'success', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'WEB BACKUP SUCCESS!!!', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aborted {
|
||||||
|
node ("site") {
|
||||||
|
jabberNotify notificationStrategy: 'aborted', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'WEB BACKUP ABORT!!!', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
node ("site") {
|
||||||
|
jabberNotify notificationStrategy: 'failure', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'WEB BACKUP ERROR!!!', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDesc(ACTION)
|
||||||
|
{
|
||||||
|
def d = [ACTION: '', BACKUP_DATE: '', END_TIME: '']
|
||||||
|
END_TIME = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
d.ACTION = "${ACTION}"
|
||||||
|
d.BACKUP_DATE = "${CURRENT_DATE}"
|
||||||
|
d.END_TIME = "${END_TIME}"
|
||||||
|
d1 = d.clone()
|
||||||
|
props << d1
|
||||||
|
println d1
|
||||||
|
d.clear()
|
||||||
|
println props
|
||||||
|
println d
|
||||||
|
println "${END_TIME}"
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendME(ACTION,STATUS)
|
||||||
|
{
|
||||||
|
emailext body: "${STATUS}",
|
||||||
|
subject: "${ACTION}",
|
||||||
|
to: 'therion@nasda.ru'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: '*/master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs: [[url: 'git@gitlab.domen:user/sys.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def scriptDir = getClass().protectionDomain.codeSource.location.path
|
||||||
|
println "${scriptDir}"
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath()
|
||||||
|
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача бэкапирует сетевые диски</b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, звонить :)
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.backup_table { margin:15px 0; border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 100%;}
|
||||||
|
.backup_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.backup_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.backup_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.backup_table .stand:hover { background:#f6faf2; }
|
||||||
|
.backup_table .col_title { width:120px; }
|
||||||
|
</style>
|
||||||
|
<table class="backup_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Задания бэкапа:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="ACTION">Бэкапирование</th>
|
||||||
|
<th abbr="BACKUP_DATE">Дата выполнения</th>
|
||||||
|
<th abbr="END_TIME">Время завершения</th>
|
||||||
|
</tr>
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="mail">
|
||||||
|
<td abbr="ACTION">${prop.ACTION}</td>
|
||||||
|
<td abbr="BACKUP_DATE">${prop.BACKUP_DATE}</td>
|
||||||
|
<td abbr="END_TIME">${prop.END_TIME}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (ALL, 1CDB, WORK)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ALL</b> - Выполнение всех задач, планировщик делает по умолчанию в выходной
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>WEB</b> - Бэкап Продуктивного и тестового сайтов
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>LINUX</b> - Бэкап Linux server with rsync
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Бэкапы тут</b> - <a href="\\\\ASTAROT.domen\\Backup">\\\\ASTAROT.domen\\Backup</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
405
jenkins/branch_ncc.groovy
Normal file
405
jenkins/branch_ncc.groovy
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
// import java.time.*
|
||||||
|
// import hudson.model.Computer.ListPossibleNames
|
||||||
|
|
||||||
|
|
||||||
|
@Field int NUMBER_OF_STANDS = 4
|
||||||
|
@Field String STAND_NAME_TEMPLATE = "nccbranch"
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
node {
|
||||||
|
label 'ncc-apps'
|
||||||
|
customWorkspace '/opt/ncc/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['Deploy', 'Prolong', 'Stop'],
|
||||||
|
description: 'Выбор действия'
|
||||||
|
)
|
||||||
|
choice(
|
||||||
|
name: 'STAND',
|
||||||
|
choices: "${getStandsNames()}",
|
||||||
|
description: 'Выбор стенда'
|
||||||
|
)
|
||||||
|
string(
|
||||||
|
name: 'VERSION',
|
||||||
|
defaultValue: '7.5',
|
||||||
|
description: 'Номер версии NCC'
|
||||||
|
)
|
||||||
|
string(
|
||||||
|
name: 'NOT_OFF',
|
||||||
|
defaultValue: getParamNotOff(),
|
||||||
|
description: 'Не выключать до указанного времени, например, "31/01 20-00"'
|
||||||
|
)
|
||||||
|
booleanParam(
|
||||||
|
name: 'clearDB',
|
||||||
|
defaultValue: false,
|
||||||
|
description: "Очистить базу данных"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 40, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CHAT_PORT = getPort("${STAND}")
|
||||||
|
DIR_STAND = "/opt/ncc/${STAND}"
|
||||||
|
CURRENT_DATE = new Date().format( 'dd.MM.yy_HHmm' )
|
||||||
|
COMPOSE_INTERACTIVE_NO_CLI = 1
|
||||||
|
COMPOSE_HTTP_TIMEOUT = 180
|
||||||
|
}
|
||||||
|
triggers{
|
||||||
|
parameterizedCron('''
|
||||||
|
02 3 * * * %STAND=nccbranch4;ACTION=Deploy;BUILD_USER=Jenkins
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Stop') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'ACTION', value: 'Stop'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
dir("${DIR_STAND}/ncc-compose/") {
|
||||||
|
sh '''
|
||||||
|
docker version
|
||||||
|
docker-compose --ansi never version
|
||||||
|
docker-compose --ansi never ps && docker-compose --ansi never down --remove-orphans --volumes || exit 0
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('PrepareDB') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'clearDB', value: 'false'
|
||||||
|
not { environment name: 'VERSION', value: '' }
|
||||||
|
environment name: 'ACTION', value: 'Deploy'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
dir("${DIR_STAND}/ncc-compose/") {
|
||||||
|
sh '''
|
||||||
|
docker version
|
||||||
|
docker-compose --ansi never version
|
||||||
|
docker login -u user -p 123 sd-docker-registry2.naumen.ru
|
||||||
|
docker login -u pavykeka -p pesogane33145713 ncc-75.nau.team
|
||||||
|
rm -rf ./createdb/*.bak || true
|
||||||
|
'''
|
||||||
|
|
||||||
|
}
|
||||||
|
dir("${DIR_STAND}") {
|
||||||
|
sh '''
|
||||||
|
echo "BUILD_ID=${BUILD_ID}" > build.properties
|
||||||
|
echo "BUILD_USER=${BUILD_USER}" >> build.properties
|
||||||
|
echo "VERSION=${VERSION}" >> build.properties
|
||||||
|
echo "NOT_OFF=${NOT_OFF}" >> build.properties
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
updateDesc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stage('Prepare') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'clearDB', value: 'true'
|
||||||
|
not { environment name: 'VERSION', value: '' }
|
||||||
|
environment name: 'ACTION', value: 'Deploy'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
dir("${DIR_STAND}/ncc-compose/") {
|
||||||
|
sh '''
|
||||||
|
docker version
|
||||||
|
docker-compose --ansi never version
|
||||||
|
docker-compose --ansi never ps && docker-compose --ansi never down --remove-orphans --volumes || exit 0
|
||||||
|
docker login -u user -p 123 sd-docker-registry2.naumen.ru
|
||||||
|
docker login -u pavykeka -p pesogane33145713 ncc-75.nau.team
|
||||||
|
sudo chown -R administrator. ./
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
dir("${DIR_STAND}") {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
sh '''
|
||||||
|
echo "BUILD_ID=${BUILD_ID}" > build.properties
|
||||||
|
echo "BUILD_USER=${BUILD_USER}" >> build.properties
|
||||||
|
echo "VERSION=${VERSION}" >> build.properties
|
||||||
|
echo "NOT_OFF=${NOT_OFF}" >> build.properties
|
||||||
|
mkdir ./ncc-compose/dbdata
|
||||||
|
sudo chown -R 100 ./ncc-compose/consul-data
|
||||||
|
sudo chown -R 777 ./ncc-compose/dbdata
|
||||||
|
sudo chown -R 777 ./ncc-compose/spool
|
||||||
|
sudo chown -R 777 ./ncc-compose/createdb
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
updateDesc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
not { environment name: 'VERSION', value: '' }
|
||||||
|
environment name: 'ACTION', value: 'Deploy'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
currentBuild.description = "${STAND} <- $VERSION <br> Не выключать до $NOT_OFF"
|
||||||
|
}
|
||||||
|
dir("${DIR_STAND}/ncc-compose/") {
|
||||||
|
writeFile file:".env_tmp", text:getEnvFile()
|
||||||
|
sh '''
|
||||||
|
cat .env_tmp > .env
|
||||||
|
docker-compose --ansi never config
|
||||||
|
docker-compose --ansi never up --build -d
|
||||||
|
sudo chown -R 777 spool
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
dir("${DIR_STAND}/ncc-compose/") {
|
||||||
|
sh 'docker-compose --ansi never ps || exit 0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Prolong') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'ACTION', value: 'Prolong'
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
currentBuild.description = "${STAND} <- Prolong <br> Не выключать до $NOT_OFF"
|
||||||
|
}
|
||||||
|
dir("${DIR_STAND}") {
|
||||||
|
sh 'sed -i -r "s|(NOT_OFF=).*|\\1${NOT_OFF}|" build.properties'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
updateDesc()
|
||||||
|
emailext (
|
||||||
|
subject: "${currentBuild.currentResult}: Pipeline ${currentBuild.fullDisplayName}",
|
||||||
|
body: "${currentBuild.currentResult} Pipeline: ${currentBuild.fullDisplayName}:\n${currentBuild.absoluteUrl}",
|
||||||
|
recipientProviders: [[$class: 'RequesterRecipientProvider']]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// def node = jenkins.model.Jenkins.instance.getNode( "ncc-ci" )
|
||||||
|
// def node_ip = node.computer.getChannel().call(new ListPossibleNames())
|
||||||
|
// println "${node_ip}"
|
||||||
|
|
||||||
|
def getParamNotOff() {
|
||||||
|
return new Date().plus(1).format( 'dd/MM ' ) + "20-00"
|
||||||
|
// LocalDateTime t = LocalDateTime.now();
|
||||||
|
// return t as String
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
def getProjectDirectory() {
|
||||||
|
return fileExists("${DIR_STAND}") ? "${DIR_STAND}" : "${DIR_STAND}"
|
||||||
|
}
|
||||||
|
|
||||||
|
def getPort(String stand) throws NumberFormatException {
|
||||||
|
int initialPort = 10000
|
||||||
|
return initialPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
}
|
||||||
|
|
||||||
|
def getEnvFile() {
|
||||||
|
String stand = "${STAND}"
|
||||||
|
int initialDbPort = 54320
|
||||||
|
int initalNCCPort = 10100
|
||||||
|
int initalNCCFPort = 10110
|
||||||
|
int initialCONSULPort = 8000
|
||||||
|
int initialWSPort = 10800
|
||||||
|
int NCCPort = initalNCCPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
int CONSULPort = initialCONSULPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
int NCCFPort = initalNCCFPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
int WSPort = initialWSPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
int dbPort = initialDbPort + (stand - STAND_NAME_TEMPLATE).toInteger()
|
||||||
|
return """
|
||||||
|
COMPOSE_PROJECT_NAME=${stand}
|
||||||
|
COMPOSE_FILE=docker-compose.yaml
|
||||||
|
chatOnSite=https://${stand}.nsd.naumen.ru/chatOnSite/
|
||||||
|
URL=https://${stand}.nsd.naumen.ru/workplace.html#/login
|
||||||
|
CHAT_PORT=${CHAT_PORT}
|
||||||
|
NCC_PORT=${NCCPort}
|
||||||
|
NCCF_PORT=${NCCFPort}
|
||||||
|
DB_PORT=${dbPort}
|
||||||
|
CONSUL_PORT=${CONSULPort}
|
||||||
|
WS_PORT=${WSPort}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: '*/master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs: [[url: 'git@gitsd.naumen.ru:ops/compose.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDesc() {
|
||||||
|
def d = [BUILD_ID: '', BUILD_USER: '', VERSION: '', NOT_OFF: '']
|
||||||
|
def props = []
|
||||||
|
for (int i = 1; i <= NUMBER_OF_STANDS; i++) {
|
||||||
|
prop = readProperties defaults: d, file: "/opt/ncc/nccbranch${i}/build.properties"
|
||||||
|
props << prop
|
||||||
|
}
|
||||||
|
println props
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def nowTime() {
|
||||||
|
return new Date().format( 'yyyy-MM-dd HH:mm' )
|
||||||
|
}
|
||||||
|
|
||||||
|
def getStandStatus(String notOff) {
|
||||||
|
if (!(notOff =~ /^[0-9][0-9]?\/[0-9][0-9] [0-9][0-9]?[-:][0-9][0-9]/)) {
|
||||||
|
standStatus = ''
|
||||||
|
} else {
|
||||||
|
yy = new Date().format( 'yyyy' )
|
||||||
|
fullDate = yy + ' ' + notOff
|
||||||
|
till = new SimpleDateFormat("yyyy dd/MM HH-mm").parse(fullDate).getTime()
|
||||||
|
now = new Date().getTime()
|
||||||
|
standStatus = (till > now) ? 'stand_busy' : 'stand_free'
|
||||||
|
}
|
||||||
|
return standStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача обновляет один из стендов из ветки</b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, то обращаться в ТПиВИ
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.stand_table { margin:15px 0; /*border: 1px solid #cdcdcd;*/ border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 90%;}
|
||||||
|
.stand_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.stand_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.stand_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.stand_table .stand:hover { background:#f6faf2; }
|
||||||
|
.stand_table .col_title { width:120px; }
|
||||||
|
.stand_table .col_date { width:150px; }
|
||||||
|
.stand_table .col_link { width:120px; }
|
||||||
|
.stand_free { background:#B4EEA1; color:#349714; }
|
||||||
|
.stand_busy { background:#FF8282; color:#B21818; }
|
||||||
|
</style>
|
||||||
|
<table class="stand_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Стенды:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="stand">Стенд</th>
|
||||||
|
<th abbr="link">Административный интерфейс admin/admin</th>
|
||||||
|
<th abbr="chatOnSite">Чат на сайте</th>
|
||||||
|
<th abbr="buildnum">Номер сборки</th>
|
||||||
|
<th abbr="userid">Пользователь</th>
|
||||||
|
<th abbr="ip">Нода</th>
|
||||||
|
<th abbr="ncc_port">Порт шины</th>
|
||||||
|
<th abbr="fx_url">Файловое Хранилище</th>
|
||||||
|
<th abbr="consul_port">Порт консула</th>
|
||||||
|
<th abbr="notoff">Не отключать</th>
|
||||||
|
</tr>
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="stand">
|
||||||
|
<td abbr="stand">${idx+1}</td>
|
||||||
|
<td abbr="link">
|
||||||
|
<a href="https://nccbranch${idx+1}.nsd.naumen.ru/workplace.html#/login">https://nccbranch${idx+1}.nsd.naumen.ru/workplace.html#/login</a>
|
||||||
|
</td>
|
||||||
|
<td abbr="chatOnSite">
|
||||||
|
<a href="https://nccbranch${idx+1}.nsd.naumen.ru/chatOnSite/">https://nccbranch${idx+1}.nsd.naumen.ru/chatOnSite/</a>
|
||||||
|
</td>
|
||||||
|
<td abbr="buildnum">${prop.BUILD_ID}</td>
|
||||||
|
<td abbr="userid">${prop.BUILD_USER}</td>
|
||||||
|
<td abbr="ip">nccbranch${idx+1}.sd.naumen.ru</td>
|
||||||
|
<td abbr="ncc_port">1010${idx+1}</td>
|
||||||
|
<td abbr="fx_url">
|
||||||
|
<a href="https://nccbranch${idx+1}.nsd.naumen.ru/fx">https://nccbranch${idx+1}.nsd.naumen.ru/fx</a>
|
||||||
|
</td>
|
||||||
|
<td abbr="consul_port">800${idx+1}</td>
|
||||||
|
<td abbr="notoff" class="${getStandStatus(prop.NOT_OFF)}">${prop.NOT_OFF}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
<tr>
|
||||||
|
<td align="right" abbr="lastupd" COLSPAN="8" class="last_update">Last updated: ${nowTime()}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо выбрать стенд для обновления и указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (Deploy, Prolong, Stop)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Deploy</b> - развертывание стенда. Используемые параметры: VERSION, NOT_OFF, крыжик clearDB (пересоздаст с настройками по умолчанию)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Prolong</b> - продление стенда. Используемые параметры: NOT_OFF
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Stop</b> - остановить стенд. По просьбе тестирования, для эмуляции сбоя. Последующий запускс стенда через Delpoy
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>STAND</b> - имя стенда
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>VERSION</b> - версия системы, заложено на будущее, а настоящее время, интеграция тестировалась только на версии 7.5
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Не отключать</b> - указать время и дату, до которой нужен стенд (поле информативное, стенд не будет автоматически выключен)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Нода</b> - данные указать в поле Адрес сервера при настройке интеграции со стороны NSD
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Порт шины</b> - данные для поля порт при настройке интеграции со стороны NSD
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Файловое хранилище</b> - Пишем в поле файлового хранилища при настроке интеграции со стороны NSD
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Логины к чату на сайте</b> - Логины в системе: <b>portal1...portal8</b> (под портальные бранчи), <b>test1...test4</b> (под ncc). Пароли для всех <b>123</b>. Менять, добавлять, логины к витринам: <b>http://nccbranch1.sd.naumen.ru:8001/</b>.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
def getStandsNames() {
|
||||||
|
def standsNames = ''
|
||||||
|
for (int i = 1; i <= NUMBER_OF_STANDS; i++) {
|
||||||
|
standsNames += "nccbranch${i}"
|
||||||
|
standsNames += "${i.equals(NUMBER_OF_STANDS) ? "" : "\n"}"
|
||||||
|
}
|
||||||
|
return standsNames
|
||||||
|
}
|
||||||
237
jenkins/getmailbox.groovy
Normal file
237
jenkins/getmailbox.groovy
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
// Misc manipulations
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
node {
|
||||||
|
label 'mail_acc'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['New-Mail', 'Delete-Mail', 'Get-Mail'],
|
||||||
|
description: 'Выбор действия, Get-Mail - только обновит список пользователей'
|
||||||
|
)
|
||||||
|
string(
|
||||||
|
name: 'email',
|
||||||
|
defaultValue: '@domain',
|
||||||
|
description: 'Желаемый email пользователя, если меняем пароль, выбираем из списка, при создании нового, выбрать свободное имя'
|
||||||
|
)
|
||||||
|
string(
|
||||||
|
name: 'MailPass',
|
||||||
|
defaultValue: '123qweASD',
|
||||||
|
description: 'Пароль пользователя, стандартный лучше не использовать, во избежании случайного подключения другого пользователя'
|
||||||
|
)
|
||||||
|
booleanParam(
|
||||||
|
name: 'NeedSharedMail',
|
||||||
|
defaultValue: false,
|
||||||
|
description: 'Нужны права на общий ящик: shared@domain'
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 40, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CURRENT_DATE = new Date().format('dd.MM.yy_HHmm')
|
||||||
|
DIR_MAIL = "./jenkins/ps1/"
|
||||||
|
username = "${BUILD_USER}"+"${BUILD_ID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('getmail') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'ACTION', value: 'Get-Mail'
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
dir("${DIR_MAIL}") {
|
||||||
|
powershell '''
|
||||||
|
./getmailbox.ps1
|
||||||
|
'''
|
||||||
|
updateDesc('mailbox.nausd')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('newmail') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'ACTION', value: 'New-Mail'
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
dir("${DIR_MAIL}") {
|
||||||
|
println "${username}"
|
||||||
|
println "${email}"
|
||||||
|
println "${MailPass}"
|
||||||
|
powershell script: "./newmailbox.ps1 $username $email $MailPass $NeedSharedMail"
|
||||||
|
updateDesc('mailbox.nausd')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('removemailbox') {
|
||||||
|
when {
|
||||||
|
beforeAgent true
|
||||||
|
environment name: 'ACTION', value: 'Delete-Mail'
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
dir("${DIR_MAIL}") {
|
||||||
|
println "${username}"
|
||||||
|
println "${email}"
|
||||||
|
println "${MailPass}"
|
||||||
|
powershell script: "./removemailbox.ps1 $email"
|
||||||
|
updateDesc('mailbox.nausd')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def updateDesc(String propertyFile) {
|
||||||
|
def keyMapping = [
|
||||||
|
DisplayName: 'UserName',
|
||||||
|
WindowsEmailAddress: 'EMail',
|
||||||
|
CustomAttribute1: 'Password',
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
def props = []
|
||||||
|
def mailInfo = [:]
|
||||||
|
readFile("${propertyFile}").split('\n').each { line ->
|
||||||
|
if (!line.isEmpty()) {
|
||||||
|
def fields = line.split(':').collect { it.trim() }
|
||||||
|
mailInfo.put(keyMapping.get(fields.first()), fields.last())
|
||||||
|
if (fields.first().equals(keyMapping.keySet().last())) {
|
||||||
|
props.add(mailInfo.clone())
|
||||||
|
mailInfo.clear()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int NUMBER_OF_MAILS = props.size();
|
||||||
|
println("NUMBER_OF_MAILS: ${NUMBER_OF_MAILS}")
|
||||||
|
props.each {
|
||||||
|
println("Mail Info <br />: ${it}")
|
||||||
|
}
|
||||||
|
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class : 'GitSCM',
|
||||||
|
branches : [[name: 'master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs : [[url: 'git@gitsd.naumen.ru:repo/scripts_tpivi.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
def scriptDir = getClass().protectionDomain.codeSource.location.path
|
||||||
|
println "${scriptDir}"
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath()
|
||||||
|
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача управляет тестовым почтовым сервером <a href="https://naupp.naumen.ru/sd/operator/#uuid:hardware\$102717801">Exchange</a></b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, то обращаться в ТПиВИ
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.mail_table { margin:15px 0; border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 100%;}
|
||||||
|
.mail_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.mail_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.mail_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.mail_table .stand:hover { background:#f6faf2; }
|
||||||
|
.mail_table .col_title { width:120px; }
|
||||||
|
</style>
|
||||||
|
<table class="mail_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Почтовые ящики:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="UserName">Имя пользователя</th>
|
||||||
|
<th abbr="EMail">Почтовый ящик пользователя</th>
|
||||||
|
<th abbr="Password">Пароль пользователя</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="mail">
|
||||||
|
<td abbr="UserName">${prop.UserName}</td>
|
||||||
|
<td abbr="EMail">${prop.EMail}</td>
|
||||||
|
<td abbr="Password">${prop.Password}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (New-Mail, Delete-Mail, Get-Mail)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>New-Mail</b> - Создание нового ящика пользователя или обновление существующего
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Delete-Mail</b> - Удаление ящика пользователя в котором отпала необходимость ( попутно вычистит ящики к которым не было подключения более года )
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Get-Mail</b> - Обновит список почтовых ящиков с сервера
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Имя пользователя</b> - Формируется из параметров сборки
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Почтовый ящик пользователя</b> - Должен быть в формате login@domain. Допускаются только символы латинского алфавита
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Пароль пользователя</b> - Не менее 8 сиволов разного регистра и цифры. Рекомендуется использовать пароль отличный от стандартного
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>shared@domain</b> - Общий почтовый ящик, подключается галочкой в сборке при создании нового или изменениии параметров текущего почтового ящиков
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Веб интерфейс для доступа</b> - <a href="https://win2016-dc-exch.domain/owa/">https://win2016-dc-exch.domain/owa/</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
8
jenkins/getmailbox.ps1
Normal file
8
jenkins/getmailbox.ps1
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
$username='adm_exch@domain'
|
||||||
|
$pwsd='superpass'
|
||||||
|
$password = ConvertTo-SecureString $pwsd -AsPlainText -Force
|
||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ($username, $password)
|
||||||
|
|
||||||
|
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://win2016-dc-exch.domain/powershell/ -Authentication Basic -AllowRedirection -Credential $Cred
|
||||||
|
Invoke-Command -Session $Session -ScriptBlock { Get-Mailbox -OrganizationalUnit domain/NSD -ResultSize Unlimited } | Format-List DisplayName, WindowsEmailAddress, CustomAttribute1 | Out-File -Encoding UTF8 mailbox.nausd
|
||||||
|
Remove-PSSession $Session
|
||||||
29
jenkins/newmailbox.ps1
Normal file
29
jenkins/newmailbox.ps1
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ('adm_exch@domain', (ConvertTo-SecureString 'superpass' -AsPlainText -Force))
|
||||||
|
|
||||||
|
$UserName=$args[0]
|
||||||
|
$Email=$args[1]
|
||||||
|
$MailPass=$args[2]
|
||||||
|
$NeedSharedMail=$args[3]
|
||||||
|
|
||||||
|
Write-Host $UserName
|
||||||
|
Write-Host $Email
|
||||||
|
Write-Host $MailPass
|
||||||
|
Write-Host $NeedSharedMail
|
||||||
|
|
||||||
|
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://win2016-dc-exch.domain/powershell/ -Authentication Basic -AllowRedirection -Credential $Cred
|
||||||
|
Import-PSSession $Session -DisableNameChecking -AllowClobber
|
||||||
|
|
||||||
|
if(Get-Mailbox $Email) { Write-Host "$Email is ALREADY create" }
|
||||||
|
else { New-Mailbox -Name "$UserName" -UserPrincipalName "$Email" -Password (ConvertTo-SecureString "$MailPass" -AsPlainText -Force) -DisplayName "$UserName" -OrganizationalUnit NSD }
|
||||||
|
Set-Mailbox "$Email" -CustomAttribute1 "$MailPass" -Password (ConvertTo-SecureString "$MailPass" -AsPlainText -Force) -Force -Confirm:$false
|
||||||
|
|
||||||
|
if($NeedSharedMail -eq "True")
|
||||||
|
{
|
||||||
|
Add-MailboxPermission -User "$Email" -AccessRights FullAccess -InheritanceType All Shared@domain
|
||||||
|
Add-ADPermission -Identity "CN=shared,OU=Shared,DC=nausd,DC=local" -User "$Email" -ExtendedRights "Send As"
|
||||||
|
Set-Mailbox Shared -GrantSendOnBehalfTo "$Email"
|
||||||
|
}
|
||||||
|
|
||||||
|
Get-Mailbox -OrganizationalUnit NSD -ResultSize Unlimited | Format-List DisplayName, WindowsEmailAddress, CustomAttribute1 | Out-File -Encoding UTF8 mailbox.nausd
|
||||||
|
|
||||||
|
Remove-PSSession $Session
|
||||||
19
jenkins/removemailbox.ps1
Normal file
19
jenkins/removemailbox.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ('adm_exch@domain', (ConvertTo-SecureString 'superpass' -AsPlainText -Force))
|
||||||
|
|
||||||
|
$Email=$args[0]
|
||||||
|
|
||||||
|
Write-Host $Email
|
||||||
|
|
||||||
|
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://win2016-dc-exch.domain/powershell/ -Authentication Basic -AllowRedirection -Credential $Cred
|
||||||
|
Import-PSSession $Session -DisableNameChecking -AllowClobber
|
||||||
|
|
||||||
|
#Invoke-Command -Session $Session -ScriptBlock { Remove-Mailbox -Identity "$Using:Email" -Permanent $true -Force -Confirm:$false }
|
||||||
|
#Invoke-Command -Session $Session -ScriptBlock { Get-Mailbox -OrganizationalUnit domain/NSD -ResultSize Unlimited } | Format-List DisplayName, WindowsEmailAddress, CustomAttribute1 | Out-File -Encoding UTF8 mailbox.nausd
|
||||||
|
# Remove email from jenkins
|
||||||
|
Remove-Mailbox -Identity "$Email" -Permanent $true -Force -Confirm:$false
|
||||||
|
# Remove Older then 360 days emails
|
||||||
|
Get-Mailbox -OrganizationalUnit domain/NSD -resultsize unlimited | Get-MailboxStatistics | ?{$_.LastLogonTime -lt (get-date).adddays(-360)} | Remove-Mailbox -Permanent $true -Force -Confirm:$false
|
||||||
|
|
||||||
|
Get-Mailbox -OrganizationalUnit NSD -ResultSize Unlimited | Format-List DisplayName, WindowsEmailAddress, CustomAttribute1 | Out-File -Encoding UTF8 mailbox.nausd
|
||||||
|
|
||||||
|
Remove-PSSession $Session
|
||||||
256
jenkins/site.groovy
Normal file
256
jenkins/site.groovy
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import groovy.transform.Field
|
||||||
|
@Field List props = []
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent none
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'ACTION',
|
||||||
|
choices: ['ALL','PROG','DOC','WORK','MOVE'],
|
||||||
|
description: 'Выбор бэкапа'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
disableConcurrentBuilds()
|
||||||
|
timestamps()
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
skipDefaultCheckout()
|
||||||
|
timeout(time: 2400, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BUILD_USER = getBuildUser()
|
||||||
|
CURRENT_DATE = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
triggers{
|
||||||
|
parameterizedCron('''
|
||||||
|
00 01 * * 6 %ACTION=ALL
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('Prepare_MASTER') {
|
||||||
|
agent {label "master"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Prepare_SITE') {
|
||||||
|
agent {label "net"}
|
||||||
|
steps {
|
||||||
|
deleteDir()
|
||||||
|
gitClone("master")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////
|
||||||
|
stage('Backup_WORK') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'WORK'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./site/site_session.ps1 WORK $env.JOB_NAME"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: 'WORK backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////
|
||||||
|
/////
|
||||||
|
stage('Backup_PROG') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'PROG'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./site/site_session.ps1 PROG $env.JOB_NAME"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: 'PROG backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////
|
||||||
|
stage('Backup_DOC') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'DOC'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
|
||||||
|
steps {
|
||||||
|
pwsh script: "./site/site_session.ps1 DOC $env.JOB_NAME"
|
||||||
|
updateDesc("${env.STAGE_NAME}")
|
||||||
|
sendME("${env.STAGE_NAME}","Backup success!!!")
|
||||||
|
telegramSend(message: 'DOC backup success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////
|
||||||
|
|
||||||
|
stage('Move_del') {
|
||||||
|
agent {label "master"}
|
||||||
|
when { beforeAgent true
|
||||||
|
anyOf {environment name: 'ACTION', value: 'MOVE'; environment name: 'ACTION', value: 'ALL'}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
mv /srv/SITE/*.7z /srv/1CSRV/
|
||||||
|
find /srv/1CSRV/ -name "*.7z" -mtime +6 -exec rm -f {} \\;
|
||||||
|
'''
|
||||||
|
telegramSend(message: 'ALL MOVE success!!!', chatId: 648920818)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'success', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Сетевые диски успешно сбэкапированы, можно забирать', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aborted {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'aborted', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Пришлось остановить', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
node ("master") {
|
||||||
|
jabberNotify notificationStrategy: 'failure', notifyCulprits: true, notifyFixers: true, notifySuspects: true, notifyUpstreamCommitters: true, extraMessage: 'Бэкап сетевых дисков завершился с ошибкой', targets: 'user@domen'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def getBuildUser() {
|
||||||
|
return currentBuild.rawBuild.getCause(Cause.UserIdCause)?.getUserId()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDesc(ACTION)
|
||||||
|
{
|
||||||
|
def d = [ACTION: '', BACKUP_DATE: '', END_TIME: '']
|
||||||
|
END_TIME = new Date().format( 'dd.MM.yy_HH:mm' )
|
||||||
|
d.ACTION = "${ACTION}"
|
||||||
|
d.BACKUP_DATE = "${CURRENT_DATE}"
|
||||||
|
d.END_TIME = "${END_TIME}"
|
||||||
|
d1 = d.clone()
|
||||||
|
props << d1
|
||||||
|
println d1
|
||||||
|
d.clear()
|
||||||
|
println props
|
||||||
|
println d
|
||||||
|
println "${END_TIME}"
|
||||||
|
def item = Jenkins.instance.getItemByFullName("${JOB_NAME}")
|
||||||
|
item.setDescription("${getDescTemplate(props)}")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def sendME(ACTION,STATUS)
|
||||||
|
{
|
||||||
|
emailext body: "${STATUS}",
|
||||||
|
subject: "${ACTION}",
|
||||||
|
to: 'therion@nasda.ru'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def gitClone(String stand) {
|
||||||
|
checkout poll: false, scm: [
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: '*/master']],
|
||||||
|
doGenerateSubmoduleConfigurations: false,
|
||||||
|
userRemoteConfigs: [[url: 'git@gitlab.domen:user/sys.git']]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def scriptDir = getClass().protectionDomain.codeSource.location.path
|
||||||
|
println "${scriptDir}"
|
||||||
|
|
||||||
|
String currentDir = new File(".").getAbsolutePath()
|
||||||
|
|
||||||
|
|
||||||
|
def getDescTemplate(List props) {
|
||||||
|
return """
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><html>
|
||||||
|
<body>
|
||||||
|
<b>Задача бэкапирует сетевые диски</b>
|
||||||
|
<div style="font-wight:bold;">
|
||||||
|
Если сборка не прошла, звонить :)
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.backup_table { margin:15px 0; border:none; border-collapse:collapse; border-spacing:0; font-size:100%; width: 100%;}
|
||||||
|
.backup_table th, .stand_table td { padding:3px 4px; border: 1px solid #cdcdcd; }
|
||||||
|
.backup_table th { text-align:center; font-weight:bold; border: 1px solid #cdcdcd; background:#f9efcf; }
|
||||||
|
.backup_table .last_update { text-align:right; font-size:75%; border:none; }
|
||||||
|
.backup_table .stand:hover { background:#f6faf2; }
|
||||||
|
.backup_table .col_title { width:120px; }
|
||||||
|
</style>
|
||||||
|
<table class="backup_table">
|
||||||
|
<tr>
|
||||||
|
<th colspan="8" style="text-align:left;">Задания бэкапа:</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th abbr="ACTION">Бэкапирование</th>
|
||||||
|
<th abbr="BACKUP_DATE">Дата выполнения</th>
|
||||||
|
<th abbr="END_TIME">Время завершения</th>
|
||||||
|
</tr>
|
||||||
|
${props.collect { prop ->
|
||||||
|
def idx = props.findIndexOf { it.equals(prop) }
|
||||||
|
return """ <tr class="mail">
|
||||||
|
<td abbr="ACTION">${prop.ACTION}</td>
|
||||||
|
<td abbr="BACKUP_DATE">${prop.BACKUP_DATE}</td>
|
||||||
|
<td abbr="END_TIME">${prop.END_TIME}</td>
|
||||||
|
</tr>"""
|
||||||
|
}.join('')}
|
||||||
|
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<br/>При запуске необходимо указать параметры:
|
||||||
|
<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ACTION</b> - выполняемое действие (ALL, 1CDB, WORK)
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>ALL</b> - Выполнение всех задач, планировщик делает по умолчанию в выходной
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>WORK</b> - Бэкап сетевого диска W (WORK)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>PROG</b> - Бэкап сетевого диска K (PROG)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>DOC</b> - Бэкап сетевого диска V (DOC)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>MOVE</b> - Перенос бэкапов в папку на сервере 1CSRV, удаление старых
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Бэкапы тут</b> - <a href="\\\\1CSRV.domen\\Backup">\\\\1CSRV.domen\\Backup</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
8
jenkins/site_session.ps1
Normal file
8
jenkins/site_session.ps1
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
$Action=$args[0]
|
||||||
|
$Job=$args[1]
|
||||||
|
Write-Host $Action
|
||||||
|
Write-Host $Job
|
||||||
|
|
||||||
|
$Cred = New-Object System.Management.Automation.PSCredential ('user', (ConvertTo-SecureString 'passwd' -AsPlainText -Force))
|
||||||
|
Invoke-Command -ComputerName 172.16.0.1 -Credential $Cred -Authentication Negotiate -ScriptBlock {..\.jenkins\workspace\site_backup\site\site_backup.ps1 $Using:Action}
|
||||||
|
|
||||||
Reference in New Issue
Block a user