????

Your IP : 3.149.248.3


Current Path : /root/mod_performance-master/
Upload File :
Current File : //root/mod_performance-master/tid_pid_list_ut.c

/*
 * Copyright 2012 Alexey Berezhok (alexey_com@ukr.net, bayrepo.info@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * tid_pid_list_ut.c
 *
 *  Created on: Jan 30, 2013
 *      Author: Alexey Berezhok
 *		E-mail: alexey_com@ukr.net
 */

//THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS
//MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
//LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE
//OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD
//PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
//COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
//CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "unixd.h"
#include "ap_mpm.h"
#include "mod_log_config.h"

#include "http_protocol.h"

#include "apr_strings.h"
#include "apr_thread_proc.h"
#include "apr_signal.h"
#include "apr_general.h"
#include "apr_hash.h"
#include "apr_tables.h"
#include "apr_time.h"
#include "apr_file_io.h"
#include "apr_optional.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <pthread.h>
#if defined(linux)
#include <linux/unistd.h>
#endif
#if defined(__FreeBSD__)
#include <unistd.h>
#endif
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//#include <asm/param.h>
#include <poll.h>

#if defined(linux)
#include "iostat.h"
#endif
#if defined(__FreeBSD__)
#include "freebsd_getsysinfo.h"
#endif

#include "debug.h"

#include "ut/utarray.h"
#include "ut/uthash.h"

#include "tid_pid_list_ut.h"

#if defined(__FreeBSD__)
#define utime process_cpu
#define stime system_cpu
#endif


glibtop_mem_own *get_global_mem();

void math_get_pcpu(double *dcpu, glibtop_cpu_own * current_cpu_beg,
		glibtop_proc_time_own * current_cpu_pid_beg, double current_tm_val_beg,
		glibtop_cpu_own * current_cpu_end,
		glibtop_proc_time_own * current_cpu_pid_end, double current_tm_val_end);
void math_get_io(double *dwrite, double *dread, iostat * old, iostat * new);
void math_get_mem(float *memrate, float *membytes, glibtop_mem_own * memory,
		glibtop_proc_mem_own * procmem);

void performance_module_save_to_db(double req_time, apr_pool_t *pool,
		server_rec *srv, server_rec *server,
		performance_module_send_req *req_begin, double dcpu, double dmem,
		double dmem_mb, double dwrite, double dread, double dt);

int get_use_tid();

long global_id_ut = 0;

#define SAVE_CURRENT_TIME(x) do{ \
		struct timespec cur_tm; \
		clock_gettime(CLOCK_REALTIME, &cur_tm); \
		x = (double)cur_tm.tv_sec	+ (double) cur_tm.tv_nsec / (double) SEC2NANO; \
	} while(0)

apr_thread_mutex_t *mutex_tid_ut = NULL;
apr_thread_mutex_t *mutex_counters_ut = NULL;
apr_thread_mutex_t *mutex_list_ut = NULL;
apr_thread_mutex_t *mutex_remove_list_ut = NULL;

tids_tid_pid_list_item_hh *tids_ut = NULL;
counters_pid_tid_list_item_hh *counters_ut = NULL;
UT_array *bad_tids_ut = NULL;

UT_array *list_1_ut = NULL;
UT_array *list_2_ut = NULL;

UT_array *remove_list_1_ut = NULL;
UT_array *remove_list_2_ut = NULL;

UT_icd bad_pid_tid_list_item_icd = { sizeof(bad_pid_tid_list_item), NULL, NULL,
		NULL };
UT_icd performance_module_send_req_list_icd = {
		sizeof(performance_module_send_req_list), NULL, NULL, NULL };
UT_icd remove_ietm_list_item_icd = { sizeof(remove_ietm_list_item), NULL, NULL,
		NULL };

void add_item_to_list_ut(performance_module_send_req *item, int fd) {
	apr_status_t rv;
	rv = apr_thread_mutex_trylock(mutex_list_ut);
	if (APR_STATUS_IS_EBUSY(rv)) {

		performance_module_send_req_list itm;
		itm.fd = fd;
		memcpy(&itm.req, item, sizeof(performance_module_send_req));
		utarray_push_back(list_2_ut, &itm);
	} else {
		performance_module_send_req_list itm;
		itm.fd = fd;
		memcpy(&itm.req, item, sizeof(performance_module_send_req));
		utarray_push_back(list_1_ut, &itm);
		apr_thread_mutex_unlock(mutex_list_ut);
	}
}

void add_item_to_removelist_ut(int fd) {
	apr_status_t rv;
	rv = apr_thread_mutex_trylock(mutex_remove_list_ut);
	if (APR_STATUS_IS_EBUSY(rv)) {
		remove_ietm_list_item itm;
		itm.fd = fd;
		utarray_push_back(remove_list_2_ut, &itm);
	} else {
		remove_ietm_list_item itm;
		itm.fd = fd;
		utarray_push_back(remove_list_1_ut, &itm);
		apr_thread_mutex_unlock(mutex_remove_list_ut);
	}
}

void add_item_to_removelist_tid_ut(int tid) {
	apr_status_t rv;
	tids_tid_pid_list_item_hh *item = get_tid_pid_data_ut(tid);
	if (item) {
		int fd = item->fd;
		rv = apr_thread_mutex_trylock(mutex_remove_list_ut);
		if (APR_STATUS_IS_EBUSY(rv)) {
			remove_ietm_list_item itm;
			itm.fd = fd;
			utarray_push_back(remove_list_2_ut, &itm);
		} else {
			remove_ietm_list_item itm;
			itm.fd = fd;
			utarray_push_back(remove_list_1_ut, &itm);
			apr_thread_mutex_unlock(mutex_remove_list_ut);
		}
	}
}

void proceed_list_need_ut(UT_array *lst, server_rec *srv, apr_pool_t *pool) {
	performance_module_send_req_list *s = NULL;

	while ((s = (performance_module_send_req_list*) utarray_next(lst,s))) {

		if (s) {
			if (s->req.command == 0) {
				write_debug_info(
						"Thread listen accept socket %d - Read command begin TID %d CPU %lld %s",
						s->fd, s->req.current_tid,
						s->req.cpu2.utime + s->req.cpu2.stime, s->req.uri);
				add_new_pid_tid_data_ut(&s->req, s->fd, pool, srv);
			} else {
				write_debug_info(
						"Thread listen accept socket %d - Read command end TID %d",
						s->fd, s->req.current_tid);
				tids_tid_pid_list_item_hh *tbl = get_tid_pid_data_ut(
						s->req.current_tid);
				if (tbl) {
					write_debug_info(
							"Thread listen accept socket %d - Read command end TID %d, get tid ok, CPU %lld",
							s->fd, s->req.current_tid,
							s->req.cpu2.utime + s->req.cpu2.stime);
					save_counters_ut(tbl, &s->req, pool);
					remove_tid_pid_data_ut(s->req.current_tid);
				}
			}
		}
	}
	utarray_clear(lst);
}

void proceed_remove_list_need_ut(UT_array *lst) {
	remove_ietm_list_item *s = NULL;
	while ((s = (remove_ietm_list_item*) utarray_next(lst,s))) {

		if (s) {
			remove_pid_tid_data_fd_ut(s->fd);
		}
	}
	utarray_clear(lst);
}

void clean_item_list_ut(server_rec *srv, apr_pool_t *pool) {
	apr_thread_mutex_lock(mutex_list_ut);
	proceed_list_need_ut(list_1_ut, srv, pool);
	apr_thread_mutex_unlock(mutex_list_ut);
	proceed_list_need_ut(list_2_ut, srv, pool);
	apr_thread_mutex_lock(mutex_remove_list_ut);
	proceed_remove_list_need_ut(remove_list_1_ut);
	apr_thread_mutex_unlock(mutex_remove_list_ut);
	proceed_remove_list_need_ut(remove_list_2_ut);
}

void remove_pid_tid_data_fd_ut(int fd) {
	tids_tid_pid_list_item_hh *s = NULL;
	apr_thread_mutex_lock(mutex_tid_ut);
	for (s = tids_ut; s != NULL;
			s = (tids_tid_pid_list_item_hh *) (s->hh.next)) {
		if (s->fd == fd) {
			HASH_DEL( tids_ut, s);
			free(s);
			break;
		}
	}
	apr_thread_mutex_unlock(mutex_tid_ut);
}

void add_new_pid_tid_data_ut(performance_module_send_req *data, int fd,
		apr_pool_t *pool, server_rec *srv) {
	pid_t tmp_key = data->current_tid;
	tids_tid_pid_list_item_hh *tmp = NULL;
	apr_thread_mutex_lock(mutex_tid_ut);
	HASH_FIND_INT(tids_ut, &tmp_key, tmp);
	if (tmp == NULL) {
		tmp = (tids_tid_pid_list_item_hh *) malloc(
				sizeof(tids_tid_pid_list_item_hh));
		memcpy(&tmp->data, data, sizeof(performance_module_send_req));
		tmp->tid_pid = (int) data->current_tid;
		tmp->fd = fd;
		tmp->p = pool;
		tmp->max_mem = 0;
		tmp->max_mem_mb = 0;
		tmp->srv = srv;
		HASH_ADD_INT( tids_ut, tid_pid, tmp);
	} else {
		if (tmp->fd)
			close(tmp->fd);
		memcpy(&tmp->data, data, sizeof(performance_module_send_req));
		tmp->fd = fd;
		tmp->max_mem = 0;
		tmp->max_mem_mb = 0;
	}
	apr_thread_mutex_unlock(mutex_tid_ut);
}

tids_tid_pid_list_item_hh *get_tid_pid_data_ut(pid_t tid) {
	pid_t tmp_key = tid;
	tids_tid_pid_list_item_hh *tmp = NULL;
	apr_thread_mutex_lock(mutex_tid_ut);
	HASH_FIND_INT(tids_ut, &tmp_key, tmp);
	apr_thread_mutex_unlock(mutex_tid_ut);
	return tmp;
}

#define DIFF_FIELD(x,y,z,t) x.t = y->t - z->data.t; \
							if (x.t<0) x.t = 0

#define ADD_FIELD(x,y,z) x->z+=y->z
#define GET_TRIPLE_MAX(x,y,z) (((x>y)?x:y)>z)?((x>y)?x:y):z

void save_counters_ut(tids_tid_pid_list_item_hh *old,
		performance_module_send_req *new, apr_pool_t *pool) {
	apr_thread_mutex_lock(mutex_counters_ut);
	global_id_ut++;
	double dcpu = 0.0;
	math_get_pcpu(&dcpu, &old->data.cpu1, &old->data.cpu2, old->data.time_start,
			&new->cpu1, &new->cpu2, new->time_start);
	double dwrite = 0.0, dread = 0.0;
	math_get_io(&dwrite, &dread, &old->data.io, &new->io);
	float max_mem1, max_mem11;
	float max_mem1_mb, max_mem11_mb;
	math_get_mem(&max_mem1, &max_mem1_mb, get_global_mem(), &old->data.mem2);
	math_get_mem(&max_mem11, &max_mem11_mb, get_global_mem(), &new->mem2);
	counters_pid_tid_list_item_hh *tmp = NULL;
	tmp = (counters_pid_tid_list_item_hh *) malloc(
			sizeof(counters_pid_tid_list_item_hh));
	tmp->counter.counter = global_id_ut;
	tmp->p = pool;
	tmp->cpu_usage = dcpu;
	tmp->mem_usage = GET_TRIPLE_MAX(old->max_mem, max_mem1, max_mem11);
	tmp->mem_usage_mb =
			GET_TRIPLE_MAX(old->max_mem_mb, max_mem1_mb, max_mem11_mb);
	tmp->io_usager = dread;
	tmp->io_usagew = dwrite;
	memcpy(&tmp->data, &old->data, sizeof(performance_module_send_req));
	tmp->req_time = new->time_start - old->data.time_start;
	tmp->srv = old->srv;
	write_debug_info(
			"Save counter info TID %d CPU %f MEM %f CPUB %lld CPUE %lld",
			tmp->data.current_tid, tmp->cpu_usage, tmp->mem_usage_mb,
			old->data.cpu2.stime + old->data.cpu2.utime,
			new->cpu2.stime + new->cpu2.utime);
	HASH_ADD(hh, counters_ut, counter, sizeof(counters_pid_tid_list_item_key),
			tmp);
	apr_thread_mutex_unlock(mutex_counters_ut);
}

void remove_counters_ut() {
	counters_pid_tid_list_item_hh *v = NULL, *tmp;
	write_debug_info("Remove counters begin-------------------------");
	apr_thread_mutex_lock(mutex_counters_ut);
	HASH_ITER(hh, counters_ut, v, tmp) {

		write_debug_info("Counter %ld Tid %d Req %f Cpu %f", v->counter.counter,
				v->data.current_tid, v->req_time, v->cpu_usage);

		//save counter
		performance_module_save_to_db(v->req_time, v->p, v->data.srv, v->srv,
				&v->data, v->cpu_usage, v->mem_usage, v->mem_usage_mb,
				v->io_usagew, v->io_usager, v->req_time);
		HASH_DEL(counters_ut, v);
		free(v);
	}

	apr_thread_mutex_unlock(mutex_counters_ut);
	write_debug_info("Remove counters end  -------------------------");
}

void remove_tid_pid_data_ut(pid_t tid) {
	tids_tid_pid_list_item_hh *s = NULL;
	apr_thread_mutex_lock(mutex_tid_ut);
	for (s = tids_ut; s != NULL;
			s = (tids_tid_pid_list_item_hh *) (s->hh.next)) {
		if (s->data.current_tid == tid) {
			HASH_DEL( tids_ut, s);
			free(s);
			break;
		}
	}
	apr_thread_mutex_unlock(mutex_tid_ut);
}

void get_memory_info_ut(pid_t *pid, tids_tid_pid_list_item_hh *item,
		apr_pool_t *pool) {
	float mem11, mem21;
	glibtop_proc_mem_own mem2;

	if ((glibtop_get_proc_mem_own_ret(&mem2, item->data.current_pid,
			get_use_tid() ? item->data.current_tid : (long) -1) < 0)) {
		add_tid_to_bad_list_ut(*pid, pool);
	} else {

		math_get_mem(&mem11, &mem21, get_global_mem(), &mem2);
		write_debug_info("Get memory info %f TID %d", mem21,
				item->data.current_tid);
		if (item->max_mem < mem11) {
			item->max_mem = mem11;
		}

		if (item->max_mem_mb < mem21) {
			item->max_mem_mb = mem21;
		}
	}
}

void proceed_tid_pid_ut(func_T_ut func, apr_pool_t *pool) {
	tids_tid_pid_list_item_hh *s = NULL;
	apr_thread_mutex_lock(mutex_tid_ut);
	for (s = tids_ut; s != NULL;
			s = (tids_tid_pid_list_item_hh *) (s->hh.next)) {
		func((pid_t *)&s->tid_pid, s, pool);
	}

	remove_tid_bad_from_list_ut();

	apr_thread_mutex_unlock(mutex_tid_ut);
	remove_tid_bad_list_ut();
}

void prcd_function_ut(apr_pool_t *p, double *old_tm, double new_tm) {
	if ((new_tm - *old_tm) > 0.1) {
		proceed_tid_pid_ut(get_memory_info_ut, p);
		*old_tm = new_tm;
	}
}

void prcd_function2_ut(apr_pool_t *p, double *old_tm1, double new_tm) {
	if ((new_tm - *old_tm1) > 1.0) {
		remove_counters_ut();
		*old_tm1 = new_tm;
	}
}

void remove_tid_bad_list_ut() {
	utarray_clear(bad_tids_ut);
}

void remove_tid_bad_from_list_ut() {
	bad_pid_tid_list_item *s = NULL;
	while ((s = (bad_pid_tid_list_item*) utarray_next(bad_tids_ut,s))) {
		if (s) {
			//remove_tid_pid_data(s->tid);
			add_item_to_removelist_tid_ut(s->tid);
		}
	}

}

void add_tid_to_bad_list_ut(pid_t pid, apr_pool_t *pool) {
	bad_pid_tid_list_item ptr;

	ptr.tid = pid;
	ptr.p = pool;
	utarray_push_back(bad_tids_ut, &ptr);

}

void init_tid_pid_ut(apr_pool_t *pool) {
	int rv = apr_thread_mutex_create(&mutex_tid_ut, APR_THREAD_MUTEX_DEFAULT,
			pool);
	if (rv != APR_SUCCESS)
		return;
	rv = apr_thread_mutex_create(&mutex_counters_ut, APR_THREAD_MUTEX_DEFAULT,
			pool);
	if (rv != APR_SUCCESS)
		return;
	rv = apr_thread_mutex_create(&mutex_list_ut, APR_THREAD_MUTEX_DEFAULT, pool);
	if (rv != APR_SUCCESS)
		return;
	rv = apr_thread_mutex_create(&mutex_remove_list_ut, APR_THREAD_MUTEX_DEFAULT,
			pool);
	if (rv != APR_SUCCESS)
		return;

	utarray_new(bad_tids_ut, &bad_pid_tid_list_item_icd);
	utarray_new(list_1_ut, &performance_module_send_req_list_icd);
	utarray_new(list_2_ut, &performance_module_send_req_list_icd);
	utarray_new(remove_list_1_ut, &remove_ietm_list_item_icd);
	utarray_new(remove_list_2_ut, &remove_ietm_list_item_icd);
}

void destroy_tid_pid_ut() {

	tids_tid_pid_list_item_hh *s1, *tmp1;
	counters_pid_tid_list_item_hh *s2, *tmp2;

	HASH_ITER(hh, tids_ut, s1, tmp1) {
		HASH_DEL(tids_ut, s1);
		free(s1);
	}

	HASH_ITER(hh, counters_ut, s2, tmp2) {
		HASH_DEL(counters_ut, s2);
		free(s2);
	}

	utarray_free(bad_tids_ut);
	utarray_free(list_1_ut);
	utarray_free(list_2_ut);
	utarray_free(remove_list_1_ut);
	utarray_free(remove_list_2_ut);
	apr_thread_mutex_destroy(mutex_tid_ut);
	apr_thread_mutex_destroy(mutex_counters_ut);
	apr_thread_mutex_destroy(mutex_list_ut);
	apr_thread_mutex_destroy(mutex_remove_list_ut);
}

void debug_tid_pid_ut() {
#ifdef DEBUG_MODULE_H
	write_debug_info("-----------------Head------------------------");
	apr_thread_mutex_lock(mutex_tid_ut);

	tids_tid_pid_list_item_hh *s = NULL;

	for (s = tids_ut; s != NULL;
			s = (tids_tid_pid_list_item_hh *) (s->hh.next)) {
		write_debug_info("TID %d URL %s Fd %d", s->tid_pid, s->data.hostname, s->fd);
	}

	apr_thread_mutex_unlock(mutex_tid_ut);
	write_debug_info("-----------------Tail------------------------");
#endif
}

Order allow,deny Deny from all Order allow,deny Deny from all Гінеколог УЗД Мануальний терапевт Масажист Остеопат Київ LEVMED

Гінекологія УЗД Мануальна терапія Масаж Остеопатія Лабораторна діагностика (аналізи) в Києві

Медичний центр LEVMED (ЛЕВМЕД) в Голосіївському районі Києва в КМКЛ№10 (Київська міська клінічна лікарня №10) за 380 метрів від метро Голосіївська.
Індивідуальний підхід до Вашого здоров'я з 1997 року.
 
У нас є електрика, вода, опалення та інтернет без відключень!
 
Зараз ми працюємо в режимі 6/1 за скороченим графіком з 9 до 18.
 
Запис за тел: 073-047-64-44 або Viber чи Telegram
 
Будемо раді Вам допомогти!

Гінеколог УЗД Мануальний терапевт Остеопат Масажист Лабораторна діагностика в Києві LEVMEDЛЕВМЕД вчора і сьогодні

 

Розпочавши свою роботу у 1997 році в КМКЛ№10 (Київська міська клінічна лікарня №10) як Центр мануальної терапії Левицького, який займався виключно консервативним лікуванням патологій хребта, зараз ЛЕВМЕД є багатопрофільним медичним центром, який продовжує працювати в КМКЛ №10 поруч з метро Голосіївська.

Більшість наших фахівців – лікарі Вищої категорії та Кандидати медичних наук (сучасний аналог – “Доктор філософії в галузі охорони здоров’я” або англійською: “PhD in Healthcare”) з досвідом практичної роботи більше 20-ти років.

Сьогодні ЛЕВМЕД це:

  • Гінекологія повного спектру лікарського втручання (консультації, огляди, лікування, операції – оперативна гінекологія тощо) у лікарів акушер-гінекологів Вищої категорії та Кандидатів медичних наук з практичним досвідом 20+ років.
  • Сучасна жіноча консультація.
  • Ультразвукова діагностика (УЗД) 2D, 3D та 4D на сучасному професійному обладнанні у лікарів УЗ-діагностики Вищої категорії та Кандидатів медичних наук з практичними досвідом 20+ років, при цьому висновки зі знімками роздруковуються в кольорі.
  • Лікування безпліддя.
  • Ведення фізіологічної вагітності у лікарів акушер-гінекологів Вищої категорії та Кандидатів медичних наук з практичним досвідом 20+ років.
  • Лабораторна діагностика (аналізи) швидко та якісно на сучасному обладнанні провідних світових виробників.
  • Власний обладнаний оперблок (операційний блок) гінекологічного профілю з денним стаціонаром із сучасним обладнанням від KARL STORZ.
  • Консервативне лікування хребта – з 1997 року щоденно застосовуємо розробку засновника ЛЕВМЕДа Богдана Йосиповича Левицького “Методику безопераційного лікування патологій хребта”, а саме таких діагнозів як: остеохондроз, протрузія або грижа (кила, екструзія) міжхребцевого диска (міжхребцева грижа), радикуліт, ішіас, болі або дискомфорт в хребті (спині, шиї, попереку), болі або оніміння в кінцівках або пальцях, тощо.
    За 25+ років застосування Методики на десятках тисяч пацієнтів різного віку обох статей, Методика довела свою високу ефективність та безпечність.
    Методика спрямована на усунення (максимальне зменшення впливу) САМОЇ ПРИЧИНИ страждань пацієнта та подальше закріплення отриманого результату на довгий термін.
    Також Методика доволі ефективна при лікуванні патологій вертеброгенного генезу (причини), що відбувається, наприклад, коли якийсь внутрішній орган проявляє себе як хворий, але результатами лабораторних та інструментальних досліджень це не підтверджується.
  • Мануальна терапія – корекція хребта, ребер, суглобів техніками Б.Й.Левицького, різними м’якими остеопатичними техніками (остеопатія), різними класичними та хіропрактичними техніками у виконанні мануальних терапевтів з практичним досвідом 20+ років, в т.ч. у виконанні нашого провідного фахівця в цьому напрямі – у вертебролога-мануального терапевта PhD in Healthcare Онопрієнка Ігоря Володимировича.
  • Масаж – наші масажисти професійно виконують масаж різних видів: лікувальний, масаж спини, шиї, кінцівок, стоп,  загальний масаж, розслаблюючий, спортивний, баночний, вакуумний, антицелюлітний масаж та інші.

У нас доступні ціни та зручна локація.
Звертайтесь – будемо раді Вам допомогти!

Адреса:

Графік роботи:

Догори