هرکسی که با Hadoop و سایر سرویس های جاوایی کار کرده حتما بارها مجبور شده مقادیر کانفیگ رو در فایل های XML دستکاری کنه. این زبان مارک آپ خیلی شبیه زبان های ساده تر مثل YAML نیست و ساختار و حتی زبان جستجو (Query) مخصوص خودش رو داره. میخایم در یک پست کوتاه نحوه تغییر مقدار یک پارامتر با استفاده از Ansible رو بررسی کنیم.
فرض کنیم فایل XML ما این شکلی باشه:
<configuration>
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.map.memory.mb</name>
<value>1024</value>
</property>
<property>
<name>mapreduce.reduce.memory.mb</name>
<value>1024</value>
</property>
</configuration>
حالا فرض کنیم میخایم مقدار mapreduce.reduce.memory.mb رو به ۹۹۶ تغییر بدیم.
در واقع داکیومنت انسیبل راه حل مستقیمی برای اینکار ارایه نکرده ولی به لطف داکیومنت های xml میدونیم که پارامتر Xpath میتونه جستجو انجام بده.
پس میام Xpath رو اینطوری تعریف میکنم:
/configuration/property[name='mapreduce.reduce.memory.mb']
در واقع دارم میگم در فرزند های نود configuration دنبال فرزند property ای بگرد که مقدار فرزند name اش 'mapreduce.reduce.memory.mb' باشه.
این ویژگی زبان و پارسر های xml هست. حتی به جای = در جای مناسب میشه از علامت بزرگتر و کوچکتر هم استفاده کرد.
حالا انسیبل ما این شکلی میشه:
- hosts: my-name-node
remote_user: root
gather_facts: False
tasks:
- name: update reducer memory
xml:
path: /root/mapred-site.xml
xpath: /configuration/property[name='mapreduce.reduce.memory.mb']
pretty_print: true
set_children:
- name: mapreduce.reduce.memory.mb
- value: "996"
کاری که تابع set_children انسیبل انجام میده اینه که کل فرزند های یک node رو حذف کنه و چیزی که بهش دادید رو اضافه کنه.
در واقع ما هرچی زیر مجموعه اون تگ <property> بود حذف کردیم و child های
<name>mapreduce.reduce.memory.mb</name>
<value>996</value>
رو اضافه کردیم.
با توجه به تصویر، نکته جالب کار اینه که ما اینجا idempotency رو حفظ کردیم به این معنی که اگر این پلی بوک رو بیشتر از ۱ بار اجرا کنیم دفعات بعدی هیچ اتفاقی نمیوفته و انسیبل change انجام نمیده.
به روز رسانی:
انسیبل از ماژول پایتون lxml برای ویرایش فایل های xml استفاده میکنه. اگر دوست دارید بدونید چطور اینکارو انجام میده نگاهی به سورس کدش بندازید:
https://github.com/cmprescott/ansible-xml/blob/d5a2d84428dea14d080f46287f0ba84144f36e89/library/xml.py#L342
به روز رسانی:
نیاز داشتم که به جای تغییر یک مقدار، یک element جدید به فایل xml اضافه کنم.
ینی یه همچین چیزی:
<configuration>
<property>
<name>mapreduce.job.counters.counter.name.max</name>
<value>1000</value>
</property>
<property> <--- new
<name>dfs.datanode.peer.stats.enabled</name> <--- new
<value>true</value> <--- new
</property> <--- new
</configuration>
اون چهارتا خطی که مشخص کردم لازمه که اضافه بشه.
تسکی که نوشتم این شکلی شد:
tasks: - name: remove disk conf if exists xml: path: /opt/hadoop/etc/hadoop/hdfs-site-test.xml xpath: /configuration/property[name='dfs.datanode.peer.stats.enabled'] state: absent - name: add disk conf xml: path: /opt/hadoop/etc/hadoop/hdfs-site-test.xml xpath: /configuration pretty_print: true add_children: - property: _: #Sub Node - name: dfs.datanode.peer.stats.enabled - value: "true"
اگر به جای add-children از set_children استفاده میکردم، همه المنت های property رو حدف میکرد و همین یدونه رو اضافه میکرد. پس از add_children استفاده کردم و برای اینکه idempotency حفظ بشه، قبلش یه تسک نوشتم که اگه همچین چیزی وجود داره حذفش کن. (احتمالا راه حل عاقلانه تری هم وجود داره).
این ـ: که دیدید برای اضافه کردن subnode تعریف شده که بتونیم برای نود property دوتا subnode دیگه تعریف کنیم.